css.joedoe.dev

Receptes

El que copio una vegada i una altra de projecte en projecte. Cada recepta té la demo funcionant al costat: si al teu navegador no es veu, és que encara no ho suporta.

Maquetació

Centrar qualsevol cosa

Funciona a tots

Dues línies i centrat en els dos eixos. Serveix igual per a una icona que per a un modal.

.centro {
  display: grid;
  place-items: center;
}

Graella que s'adapta sola

Funciona a tots

Tantes columnes com hi càpiguen, sense media queries. El min() evita que surti en mòbils estrets.

.rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 12rem), 1fr));
  gap: 1rem;
}

Peu sempre a baix

Funciona a tots

Amb poc contingut el peu no es queda surant a mitja pantalla. dvh té en compte la barra del navegador del mòbil.

body {
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
}

Proporció fixa

Funciona a tots

S'ha acabat el truc del padding-top: 56.25%. La imatge és quadrada i es veu en 16:9: object-fit: cover la retalla en lloc de deformar-la (va en img o video, en un div no fa res).

.portada {
  aspect-ratio: 16 / 9;
  width: 100%;
  object-fit: cover;
}

Container queries

Funciona a tots

El component canvia segons l'espai que té, no segons la pantalla. Arrossega la cantonada de la caixa per veure-ho.

.tarjeta-wrap { container-type: inline-size; }

.tarjeta { display: grid; gap: 1rem; }

@container (min-width: 22rem) {
  .tarjeta { grid-template-columns: 6rem 1fr; }
}

Targetes alineades amb subgrid

Funciona a tots

Títols de longitud diferent i tot i així tots els botons a la mateixa alçada.

.tarjetas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 1rem;
}

.tarjetas > * {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
}

Carrusel sense JavaScript

Funciona a tots

scroll-snap fa que cada targeta encaixi en deixar-la anar. Amb el dit o amb el trackpad.

.carrusel {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
}

.carrusel > * {
  flex: 0 0 80%;
  scroll-snap-align: center;
}

Àncores que no s'amaguen sota la capçalera

Funciona a tots

Amb una capçalera fixa, el títol on saltes queda tapat. scroll-margin el baixa el just.

html { scroll-behavior: smooth; }

:target,
h2[id] {
  scroll-margin-top: 5rem;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

Text

Tallar el text a N línies

Funciona a tots

Porta prefix -webkit- però funciona a tots. Per a una sola línia, text-overflow: ellipsis.

.tres-lineas {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

Títols equilibrats

Funciona a tots des de fa poc

Sense aquella última línia amb una paraula sola. pretty fa el mateix per a paràgrafs; on encara no arriba, simplement no fa res.

h1, h2, h3 {
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

Text amb degradat

Funciona a tots

Amb color: transparent sense més, un navegador que no sàpiga retallar el fons deixaria el text invisible. Dins de @supports només s'aplica on funciona; a la resta es veu en lila llis.

.degradado {
  color: #663399;
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
  .degradado {
    background: linear-gradient(90deg, #663399, #e86f9b);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

Selectors i cascada

El pare segons els fills: :has()

Funciona a tots

El selector que vam demanar durant vint anys. Aquí el camp es marca en vermell quan el que escrius no és un correu.

.campo:has(input:user-invalid) {
  border-color: crimson;
}

.campo:has(input:user-invalid) .error {
  display: block;
}

Ordenar la cascada amb @layer

Funciona a tots

El d'una capa posterior guanya sempre, tingui l'especificitat que tingui. Adeu als !important per trepitjar un framework.

@layer reset, framework, mio;

@layer framework {
  #app .boton { background: gray; }
}

@layer mio {
  .boton { background: rebeccapurple; }
}

Niar sense Sass

Funciona a tots

El CSS ja nia sol. El & funciona com a Sass, i les media queries poden anar a dins.

.boton {
  background: rebeccapurple;
  color: white;

  &:hover { background: color-mix(in oklch, rebeccapurple, black 20%); }

  .icono { margin-inline-end: .4em; }

  @media (width < 30rem) { width: 100%; }
}

Moviment

Animar l'entrada d'un popover

Funciona a tots des de fa poc

@starting-style dona el punt de partida d'una cosa que acaba d'aparèixer, i allow-discrete deixa animar display. Tot sense JS.

[popover] {
  transition: opacity .25s, translate .25s,
    display .25s allow-discrete, overlay .25s allow-discrete;
  opacity: 0;
  translate: 0 1rem;
}

[popover]:popover-open {
  opacity: 1;
  translate: 0 0;

  @starting-style {
    opacity: 0;
    translate: 0 1rem;
  }
}

Aparèixer en fer scroll

Encara falta algun navegador

Una animació que avança amb l'scroll en lloc d'amb el temps. Sense IntersectionObserver. Fes scroll dins la demo.

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .aparece {
      animation: aparece linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 30%;
    }
  }
}

@keyframes aparece {
  from { opacity: 0; scale: .8; }
}

Respectar «reduir el moviment»

Funciona a tots

Hi ha gent a qui les animacions marejen de debò. Això les apaga gairebé del tot si ho han demanat al sistema.

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

Color

Clar i fosc amb light-dark()

Funciona a tots des de fa poc

Cada color diu les seves dues versions en una línia, i color-scheme fa que formularis i scrollbars l'acompanyin.

:root {
  color-scheme: light dark;
  --fondo: light-dark(#fbfaf7, #17121f);
  --texto: light-dark(#221a2e, #ece6f5);
  --acento: light-dark(rebeccapurple, #c9a7f2);
}

body {
  background: var(--fondo);
  color: var(--texto);
}

Estats amb color-mix()

Funciona a tots

Un sol color de marca i el hover, l'actiu i el fons suau en surten. Barrejar en oklch dona tons més nets.

.boton {
  --c: rebeccapurple;
  background: var(--c);
}
.boton:hover  { background: color-mix(in oklch, var(--c), white 15%); }
.boton:active { background: color-mix(in oklch, var(--c), black 20%); }
.suave        { background: color-mix(in oklch, var(--c) 12%, transparent); }

Formularis del color de la marca

Funciona a tots

Checkboxes, ràdios, sliders i barres de progrés natius, amb el teu color i sense refer-los.

:root {
  accent-color: rebeccapurple;
}

Accessibilitat

Focus visible només amb teclat

Funciona a tots

No treguis mai l'outline sense més. Amb :focus-visible l'anell surt en tabular i no en fer clic.

:focus-visible {
  outline: 3px solid rebeccapurple;
  outline-offset: 3px;
  border-radius: 4px;
}

Amagat a la vista, no al lector de pantalla

Funciona a tots

Per al text d'un botó que només és una icona. display: none l'amagaria també per a qui no hi veu.

.solo-lectores {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

↑↓ moure's · ↵ obrir · esc tancar