CSS 2023

Alle demoer virker i Chrome Canary

CSS 2023

  • Fra "CSS3" til "CSS4"
    • CSS-arkitektur & new responsive
    • HDR-faver & farve-funktioner
  • CSS vs. JavaScript
    • Interaktion & performance
  • Fortjener CSS mere opmærksomhed?

Container queries

Scroll-driven animation

View Transitions

Cascade layers

<dialog>

popover

anchoring

@layer theme {
  html {
    --color: oklch(80% 2 220);
  }

  * {
    text-box-trim: both;
    text-box-edge: cap alphabetic;
  }
}

@layer component {
  div {
    margin-trim: block;
    padding: 2rlh;

    :is(article):has(&) {
      --rainbow-gradient: in oklch longer hue, red 0 0;
      
      padding-inline: max(1rem, (100% - 800px) / 2);
      background: linear-gradient(45deg var(--rainbow-gradient));
      container: div / inline-size;
    }

    :is(article) & {
      border: 2px solid color(display-p3 1 none 1);
      background: oklch(80% .37 220 / 25%);

      @container div (inline-size > 35rem) {
        font-size: calc(1rem * pow(1.25, 1));
      }
    }

    :is(aside) {
      container: aside / sticky;
      position: sticky;
      inset-block-start: 0;
      
      @container aside state(stuck) {
        & h2 {
          background: red;
        }
      }
    }

    :is(h2, h3) {
      inline-size: 17ch;
      text-wrap: balance;
    }

    > h2 {
      background: hsl(from var(--color) h s calc(l + 10%));
    }

    :not(article) & :nth-child(2 of .paragraph) {
      &:first-letter {
        initial-letter: 3;
        margin-inline-end: 1ch;
      }
    }

    & section {
      max-inline-size: 400px;

      > p {
        text-wrap: pretty;
      }

    }
  }
}
@layer theme {
  html {
    --color: oklch(80% 2 220);
  }

  * {
    text-box-trim: both;
    text-box-edge: cap alphabetic;
  }
}

@layer component {
  div {
    margin-trim: block;
    padding: 2rlh;

    :is(article):has(&) {
      --rainbow-gradient: in oklch longer hue, red 0 0;
      
      padding-inline: max(1rem, (100% - 800px) / 2);
      background: linear-gradient(45deg var(--rainbow-gradient));
      container: div / inline-size;
    }

    :is(article) & {
      border: 2px solid color(display-p3 1 none 1);
      background: oklch(80% .37 220 / 25%);

      @container div (inline-size > 35rem) {
        font-size: calc(1rem * pow(1.25, 1));
      }
    }

    :is(aside) {
      container: aside / sticky;
      position: sticky;
      inset-block-start: 0;
      
      @container aside state(stuck) {
        & h2 {
          background: red;
        }
      }
    }

    :is(h2, h3) {
      inline-size: 17ch;
      text-wrap: balance;
    }

    > h2 {
      background: hsl(from var(--color) h s calc(l + 10%));
    }

    :not(article) & :nth-child(2 of .paragraph) {
      &:first-letter {
        initial-letter: 3;
        margin-inline-end: 1ch;
      }
    }

    & section {
      max-inline-size: 400px;

      > p {
        text-wrap: pretty;
      }

    }
  }
}

Det ligner CSS, men det føles som et nyt sprog

— "CSS4" måske?

"CSS4"

2012 - 2017

Intrinsic web

2017 -

layout?

"CSS4"

Intrinsic web

2017 -

layout?

"CSS4"

2023 - 

Intrinsic web

Robust, pålidelig og fleksibel

CSS-arkitektur

  • Nesting
  • Cascade layers

Nesting

Nesting

meget snart!

Nesting

section {
  border: 2px solid red;
  
  
    color: red;
  }
}

h2 {

(✅)

Nesting

section {
  border: 2px solid red;
  
  
    color: red;
  }
}

&
h2 {

Nesting

section {
  border: 2px solid red;
  
  
    color: red;
  }
}

>
h2 {

Nesting

section {
  border: 2px solid red;
  
  
    color: red;
  }
}

:is(  )
h2 {

Nesting

  1. Red
  2. Blue
  3. The syntax is invalid

Hoisted

Nesting

.feature-list {
    display: grid;
    grid: "stack";

    :is(img, ul) {
      grid-area: stack;
    }

    > ul {
      z-index: 1;
      place-self: center start;
      
      > li {
        animation: opacity linear both;
        animation-timeline: view();
        
        &:hover {
          opacity: .8;
        }
      }
    }
  }

Co-location

Nesting

Nesting

Jeres tur

Løsning

Nesting

Foreslået løsning

article {
  padding: 1rem;
  background: aliceblue;
  color: darkslateblue;
  
  :nth-child(even) > & {
    color: aliceblue;
    background: darkslateblue;
  }
}

Cascade layers

Reset / Normalize

Jeres tur

The cascade

Lad den være åben

Quiz

p:not(#id) {
  color: green;
}

p.class {
  color: red;
}
<p class="class">text</p>

<p id="id">last text</p>

The cascade

Author styles

0,0,0

0,0,1

🤷‍♂️

Cascade layers

@layer reset {
  /* ... other reset rules */
  
  input[type="text"] {
    border: 1px solid gray;
  }
}

@layer components {
  /* ... other component rules */
  
  .my-input {
    border: 4px solid blue;
  }
}

order matters

Cascade layers

@layer reset, components, utilities;

@layer reset {
  /* ... other reset rules */
  
  input[type="text"] {
    border: 1px solid gray;
  }
}

@layer components {
  /* ... other component rules */
  
  .my-input {
    border: 4px solid blue;
  }
}

configure up front

1

2

3

Cascade layers

@layer reset, components, utilities;

@layer components {
  /* ... other component rules */
  
  .my-input {
    border: 4px solid blue;
  }
}

@layer reset {
  /* ... other reset rules */
  
  input[type="text"] {
    border: 1px solid gray;
  }
}

configure up front

1

2

3

Cascade layers

@layer reset, components, states;

@layer components {
  /* Buttons, inputs etc. */
}

@layer states {
  /* Make sure stats override, no matter specificity count */
  :disabled {
    background-color: #ddd;
    color: #999;
  }
  
  :focus-visible {
    outline: 2px solid var(--focus-color, currentColor);
    outline-offset: 2px;
  }
}

Cascade layers

@layer reset, theme, global, layout, components, utilities, states;

New responsive

  • Subgrid
  • Container queries
  • :has()
  • (nth-child of S)

Grid

.grid {
  align-items: last baseline;
  grid-template-columns: subgrid;
}

Nye funktionaliteter

Baseline alignment

Subgrid

meget snart!

Subgrid

Loading...

Subgrid

Loading...

Subgrid

Loading...

.subgrid {
  display: grid;
  grid-template-rows:
    [system-status] 3.5rem
    [primary-nav] 3rem
    [primary-header] 4rem
    [main] auto
    [footer] 4rem
    [system-gestures] 2rem;
  grid-template-columns:
    [fullbleed-start] 1rem
    [main-start] auto
    [main-end] 1rem
    [fullbleed-end];
}

Subgrid

nav {
  grid-area: primary-nav / fullbleed;
  
  display: grid;
  grid-template-columns: subgrid;
}

nav > .content {
  grid-area: main;
}

Subgrid

Jeres tur

Subgrid-øvelse 1

Løsning

Foreslået løsning

Subgrid-øvelse 1

body {
  display: grid;
  grid-template-rows: auto 1fr auto;
  grid-template-columns:
    [full-start]
      1fr
    [content-start]
      minmax(0, 800px)
    [content-end]
      1fr
    [full-end];
  gap: 1rlh;
  
  > * {
    grid-column: content;
  }
}

header,
main,
footer {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: full;
  align-content: start;
  
  > * {
    grid-column: content;
  }
}

.full-bleed {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: full;
  
  > * {
    grid-column: content;
  }
}

Jeres tur

Subgrid-øvelse 2

Løsning

Foreslået løsning

Subgrid-øvelse 2

.cards {
  display: grid;
  gap: 1rlh;
  
  @media (width > 600px) {
    grid-template-columns: repeat(3, 1fr);
  }
}

.card {  
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
}

truly flexible

Container queries

Container queries

vs media queries

Container queries

Container queries

@container (inline-size > 40em) {
  .card {
    flex-direction: column;
  }
}

width

range

@media (20em < width <= 40em) {
  ...
}

Container queries

.card-wrapper {
  container-type: inline-size;
}

@container (inline-size > 40em) {
  .card {
    flex-direction: column;
  }
}

Container queries

Container queries

Container queries

.card-wrapper {
  container-type: inline-size;
  container-name: main;
}

@container main (inline-size > 40em) {
  .card {
    flex-direction: column;
  }
}

Container queries

.card-wrapper {
  container: main / inline-size;
}

@container main (inline-size > 40em) {
  .card {
    flex-direction: column;
  }
}

flere navne

Container queries

Container query units

.card-wrapper {
  container: main / inline-size;
}

.card {
  font-size: clamp(1.45rem, 6cqi, 1.75rem);
}

cqw, cqh, cqi, cqb, cqmin, cqmax

6 % af inline (bredden)

Container query units

Container query units

Container queries

Container queries

Jeres tur

li:nth-last-child(odd):first-child

:has()

meget snart!

:has()

:has()

artcle:has(> img) {}

p:not(article > *) { ... }

article > :is(.class, #id) { ... }

:where(#article) a { ... }

:is(), :where(), :not()

:has()

article:has(h2) { ... }

article:not(:has(h2)) img { ... }

article:has(> :last-child:nth-child(3)) { ... }

:has()

Hvad styler vi her?

:has()

:has()

:has()

:has()

:has()

:has()

:has()

body {
  display: grid;
}

body:has(main + aside) {
  grid-template-columns: 1fr 300px;
}
body:has(aside + main) {
  grid-template-columns: 300px 1fr;
}

:has()

:root:has(dialog[open]) {
  overflow: hidden;
}

:has()

:nth of S

HDR Colors

  • Wide color gamut
  • Color functions
  • Gradients
.valid-css-color-function-colors {
  --srgb: color(srgb 1 1 1);
  --srgb-linear: color(srgb-linear 100% 100% 100% / 50%);
  --display-p3: color(display-p3 1 1 1);
  --rec2020: color(rec2020 0 0 0);
  --a98-rgb: color(a98-rgb 1 1 1 / 25%);
  --prophoto: color(prophoto-rgb 0% 0% 0%);
  --xyz: color(xyz 1 1 1);
}

Color spaces

.most-hyped {
  --display-p3: color(display-p3 1 1 1);
  --rec2020: color(rec2020 0 0 0);
}

Color spaces

P3 color gamut

P3 color gamut

oklab, oklch

HDR colors

.element {
  background: oklch(80% .2 220);
}

lightness

chroma

hue

oklch()

oklch()

oklch()

oklch()

In OKLCH all backgrounds with L≥87% have good contrast with black text.

oklch()

oklch()

Gradients

in oklch

Gradients

color-mix()

color-mix()

color-mix()

color-mix()

Linear mapping

color-mix()

Linear mapping

color-mix()

Løsning

color-mix()

Foreslået løsning

--bg:
  color-mix(
    in oklch,
    var(--color-a),
    var(--color-b) var(--percent)
  )
;

color-mix()

Typography

  • text-box-trim
  • margin-trim
  • text-balance
  • lh, rlh
  • cap, rcap

How do you center a div?

Centering stuff

😩

.element {
  display: flex;
  gap: 1.25ch;
  align-items: center;
}

????

How do you center this?

Centering stuff

😩

Overskrift

Morbi tortor mi, semper id magna quis, auctor auctor purus. Nam pellentesque vulputate lectus, et euismod est tincidunt sed.

Overskrift

Overskrift

Overskrift

line box

giraf

Figma: Vertical trim

text-box-trim

no more alignment issues

h1 { 
 text-box-trim: both;
 text-edge: cap alphabetic;
}

🥹

Overskrift

Morbi tortor mi, semper id magna quis, auctor auctor purus. Nam pellentesque vulputate lectus, et euismod est tincidunt sed.

text-box-trim

h1 {
  text-box-trim: both;
  text-edge: cap alphabetic;
  /* future? */
  /* text-box-trim: cap alphabetic; */
}

text-wrap

.balance {
  text-wrap: balance;
}

.pretty {
  text-wrap: pretty;
}

lh & rlh

Vertical rythm

article {
  padding: 2rlh;
  gap: 1rlh;
}

lh & rlh

Multi-line colors

cap & rcap

CSS Units

  • em, rem, ex, rex, cap, rcap, ch, rch, ic, ric, lh, rlh,

  • vw, vh, vi, vb, vmin, vmax,

  • cqw, cqh, cqi, cqb, cqmin, cqmax,

  • cqem, cqlh, cqex, cqch ...

Nye viewport units

Fra `vh` til `dvh`

Nye viewport units

Fra `vh` til `dvh`

CSS vs JavaScript

Intrinsic web

Hvorfor griber man typisk fat i JS?

Trigonometri i CSS

Trigonometri i CSS

Trigonometri i CSS

Math Expressions

Comparison Function
min(), max(), clamp()

Stepped Value Functions

round(), mod(), rem()

Trigonometric Functions
sin(), cos(), tan(), asin(), acos(), atan(), atan2()

Exponential Functions
pow(), sqrt(), hypot(), log(), exp()

Sign-Related Functions
abs(), sign()

Numeric Constants
e, pi

Degenerate Numeric Constants
infinity, -infinity, NaN

Er CSS snart et programmeringssprog? 😉

Inert, Esc dismiss,
top-layer

<dialog>

<dialog>

Loading...

Animation

  • Individual transforms
  • @property
  • animation-composition
  • linear()
  • Scroll Driven Animations
  • View Transitions
.box {
  rotate: 45deg;
  translate: -50% -50%;
  scale: 1.1;
}

.box:hover {
  rotate: 0deg;
}

Individual Transforms

@property --colorPrimary {
  syntax: '<color>';
}

@property

@property --colorPrimary {
  syntax: '<color>';
  initial-value: magenta;
}

@property

@property --colorPrimary {
  syntax: '<color>';
  initial-value: magenta;
  inherits: false;
}

@property

@property

@property --colorPrimary {
  syntax: '<color>';
  initial-value: magenta;
  inherits: false;
}

— typed Custom Properties

Loading...

@property

@property --colorPrimary {
  syntax: '<color>';
  initial-value: magenta;
  inherits: false;
}

— typed Custom Properties

Loading...

@property

@property --colorPrimary {
  syntax: '<color>';
  initial-value: magenta;
  inherits: false;
}

— typed Custom Properties

Loading...

OBS! @property performance

Composition

Composition

Composition

Linear()

Easing

Linear()

Easing

Linear()

Easing

Scroll-driven animation

Optimized composited scroll animations

Not blocking main thread

Scroll-driven animation

Scroll-driven animation

Loading...

Scroll-driven animation

Loading...

Scroll-driven animation

Loading...

Scroll-driven animation

Loading...

Scroll-driven animation

Loading...

.element {
  animation: progress linear;
  animation-timeline: scroll(block root);
}

Scroll-driven animation

Brug scrollbar som tidslinje

.element {
  animation: progress linear;
  animation-timeline: scroll();
}

Scroll-driven animation

Brug scrollbar som tidslinje

.element {
  animation: reveal linear;
  animation-timeline: view();
  animation-range: cover 0% entry 100%;
}

Scroll-driven animation

Hold øje med et element ift. viewporten

Scroll-driven animation

Scroll-driven animation

Jeres tur

section > * {
  animation:
    entry linear,
    exit linear;
  animation-timeline: view();
  animation-range: [...];
}

Optimized flip-animations

View transitions

View Transitions

Loading...

View Transitions

Loading...

View Transitions

Loading...

View Transitions

Loading...

View Transition

.box-1 {
  view-transition-name: box-1;
}

.box-2 {
  view-transition-name: box-2;
}

View Transition

.box-1 {
  view-transition-name: box-1;
}

.box-2 {
  view-transition-name: box-2;
}
btn.addEventListener("click", () => {
  document.startViewTransition(_ => grid.classList.toggle("col-count"));
});

View Transition

.box-1 {
  view-transition-name: box-1;
}

.box-2 {
  view-transition-name: box-2;
}
btn.addEventListener("click", () => {
  if (document.startViewTransition) {
    document.startViewTransition(_ => grid.classList.toggle("col-count"));
  } else {
    grid.classList.toggle("col-count");
  }
});

View Transition

.box-1 {
  view-transition-name: box-1;
}

::view-transition-new(box-1) {
  animation: scale-in .5s both;
}

::view-transition-old(box-1) {
  animation: scale-in .5s both reverse;
}

View Transition

View Transition (MPA)

View Transition

CSS Workshop 2023

By Dannie Vinther

CSS Workshop 2023

Workshop KEA

  • 82