CSS 2023
Nogle demo kræver Chrome Canary
CSS 2023
-
Fra "CSS3" til "CSS4"
- New responsive, CSS-arkitektur,
HDR farver, typografi
- New responsive, CSS-arkitektur,
-
CSS vs. JavaScript
- Math
- Component-arkitektur
- Interaktion & performance
- Fortjener CSS mere opmærksomhed?
Container queries
Scroll-driven animation
View Transitions
HDR colors
Typografi
Cascade layers
<dialog>
popover
anchoring
State/style queries















@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
New responsive
- Flex & grid
- Container queries
- (Viewport units)
- Style queries
- :has()
- (nth-child of S)

Flexbox & grid
.grid {
align-items: last baseline;
grid-template-columns: subgrid;
grid-template-rows: masonry;
reading-order-items: grid rows;
}

Flexbox & grid
.grid {
align-items: last baseline;
grid-template-columns: subgrid;
grid-template-rows: masonry;
reading-order-items: grid rows;
}
Nye funktionaliteter
Baseline alignment


.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

Subgrid
Loading...
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
Subgrid
Loading...
Subgrid
Loading...
.masonry {
display: grid;
grid-template-columns:
repeat(auto-fill, minmax(14rem, 1fr));
grid-template-rows: masonry;
}
Masonry

.masonry {
display: grid;
grid-template-columns: 1fr 2fr 3fr;
grid-template-rows: masonry;
}
Masonry

Source order
Fleksibelt nok?



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 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


Sidenote: Viewport units
dvh > vh
Container queries
Containment spec
enables querying a parent's style values
enables querying a parent's state
enables querying a parent's size
@container (inline-size > 40em)
@container style(font-style: italic)
@container state(stuck)
snapped, overflowing
Containment spec
enables querying a parent's state
Style queries
@container style(--theme: fun) {
}
Style queries
@container style(--theme: fun) {
button {
background: var(--bg);
border: thick solid mediumvioletred;
border-radius: 1em 0;
box-shadow: var(--shadow-xl);
font-weight: bold;
padding: 0.5em 1em;
}
}
.fun-section {
--theme: fun;
}
Style queries
@container style(--theme: fun) {
button {
background: var(--bg);
border: thick solid mediumvioletred;
border-radius: 1em 0;
box-shadow: var(--shadow-xl);
font-weight: bold;
padding: 0.5em 1em;
}
}
.fun-section {
--theme: fun;
}
Style queries
<article style="--quote-type: highlight">
<div class="blockquote">
<blockquote>
Underline your fucking links you sociopaths.
</blockquote>
<p><em>Heydon Pickering</em></p>
</div>
</article>
Style queries
Style queries

Style queries
:root {
--theme: light;
}
site-header {
--nav-style: main;
}
.intro {
--paragraph-style: initial-letter;
}
.selected-posts {
--card-style: large;
}
applying global, per page, and per section styles in CSS
Fewer presentational class names
Lea Verou
Manuel Matuzović
Style queries
Manuel Matuzović
Style queries
.navigation__menu:has(:nth-child(6)) {
--show-menu: true;
}
@container menu (50ch <= inline-size <= 80ch) {
@container not style(--show-menu: true) {
.navigation__menu button {
display: none;
}
.navigation__menu ul {
display: flex;
}
}
}
: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()
:has()

:has()

:has()
:has()
:has()
:has()
:has()
:root:has(dialog[open]) {
overflow: hidden;
}
:has()
:nth of S
CSS-arkitektur
- Nesting
- Cascade layers
- @scope
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

- Red
- Blue
- 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
Cascade layers

"reset" / "normalize"



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;
Quiz
p {
color: green;
}
#context {
color: red;
}
<div id="context">
<p>Some text</p>
</div>
@scope
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
Loading...
@scope
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
Loading...
@scope
@scope (.media-block) to (.content) {
img { border-radius: 50%; }
}
<div class="media-block">
<img src="..."> <!-- .media-block scope -->
<div class="content"> <!-- scope end -->
<img src="..."> <!-- NOT in scope -->
</div>
</div>
Donut scope
@scope
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
Loading...
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

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
Gradients
color-mix()

color-mix()

color-mix()

color-mix()
Linear mapping
color-mix()
Linear mapping
Relative color syntax

Typography
- text-box-trim
- margin-trim
- text-balance
- lh, rlh & cap
- initial-letter
- color fonts
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; */
}



margin-trim


margin-trim

article {
margin-trim: block;
}
Ikke mere * > * eller display: grid;
text-wrap
h1 {
text-wrap: balance;
}
p {
text-wrap: pretty;
}

lh & rlh
Vertical rythm
article {
padding: 2rlh;
gap: 1rlh;
}

lh & rlh
Multi-line colors
Initial letter
p:first-letter {
initial-letter: 3;
}
Initial letter
COLRv1 & font-palette
@font-face {
font-family: 'Rocher';
src: url('/fonts/RocherColorGX.woff2');
}
h1 {
font-family: "Rocher";
}

COLRv1 & font-palette
@font-face {
font-family: 'Rocher';
src: url('/fonts/RocherColorGX.woff2');
}
h1 {
font-family: "Rocher";
}
COLRv1 & font-palette
@font-palette-values --pink {
font-family: 'Rocher';
base-palette: 1;
}
h1 {
font-palette: --pink;
}
COLRv1 & font-palette
@font-palette-values --pink {
font-family: 'Rocher';
base-palette: 1;
}
h1 {
font-palette: --pink;
}

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,
@starting-style
<dialog>

<dialog>
Loading...
<dialog>
Loading...
@starting-style
Loading...
Tøjring af popovers
Popover & Anchoring
Popover & Anchoring
Loading...
Popover & Anchoring
Loading...
Popover & Anchoring
Loading...
Popover & Anchoring

<selectmenu>
<option value="r" checked>Red</option>
<option value="g">Green</option>
<option value="b">Blue</option>
</selectmenu>
Select Menu
Stylable dropdowns
selectmenu {
&:open { ... }
&::part(button) { ... }
&::part(marker) { ... }
> option::before { ... }
> option[value="r"]::before { background: red; }
}
Select Menu
Stylable dropdowns
built-in
selectmenu {
&:open { ... }
[slot="button"] { ... }
[behavior="selected-value"] { ... }
[slot="listbox"] { ... }
[popover] option { ... }
}
Select Menu
slots & behavior
custom-built
<selectmenu>
<button slot="button" behavior="button">
<span behavior="selected-value"></span>
</button>
<div slot="listbox">
<div popover behavior="listbox">
<option value="one">one</option>
<option value="two">two</option>
</div>
</div>
</selectmenu>
<selectmenu>
<button slot="button" behavior="button">
<span behavior="selected-value"></span>
</button>
<div slot="listbox">
<div popover behavior="listbox">
<option value="one">one</option>
<option value="two">two</option>
</div>
</div>
</selectmenu>
Select Menu
slots & behavior
selectmenu {
&:open { ... }
[slot="button"] { ... }
[behavior="selected-value"] { ... }
[slot="listbox"] { ... }
[popover] option { ... }
}
Select Menu
Loading...
const Popover = ({ children, id, type = "auto" }) => {
return (
<div popover={type} id={id}>
<button popovertarget={id} popovertargetaction="hide">
<span aria-hidden="true">×</span>
<span className="sr-only">Close Popover</span>
</button>
{children}
</div>
);
};
Popover & Anchoring
.jsx example
const Popover = ({ children, id, type = "auto" }) => {
return (
<div popover={type} id={id}>
<button popovertarget={id} popovertargetaction="hide">
<span aria-hidden="true">×</span>
<span className="sr-only">Close Popover</span>
</button>
{children}
</div>
);
};
Popover & Anchoring
const App = () => {
const popoverId = crypto.randomUUID();
return (
<main>
<h1>Popover API w/ React</h1>
<button popovertarget={popoverId}>Toggle Popup!</button>
<Popover type="auto" id={popoverId}>
I'm a Popover!
<span className="wave">👋</span>
</Popover>
</main>
);
};
.jsx example
Popover, anchoring, @starting-style
Selectmenu
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...
Composition
Composition
@property performance
Composition
Linear()
Easing
Linear()
Easing
Linear()
Easing


Scroll driven animation
Optimized composited scroll animations
Scroll driven animation
Not blocking main thread
Scroll driven animation
Loading...
Scroll driven animation
Loading...
Scroll driven animation
Loading...
Scroll driven animation
Loading...
.element {
animation: rotate linear;
animation-timeline: scroll(block root);
}
Scroll driven animation
Brug scrollbar som tidslinje
.element {
animation: rotate linear;
animation-timeline: view(inline);
animation-range: entry;
}
Scroll driven animation
Hold øje med et element ift. viewporten
Scroll driven animation
Optimized flip-animations
View transitions
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


Hvad mangler?

CSS2023
By Dannie Vinther
CSS2023
- 111