Nogle demo kræver Chrome Canary
@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?
2012 - 2017
Intrinsic web
2017 -
Intrinsic web
2017 -
2023 -
Intrinsic web
Robust, pålidelig og fleksibel
.grid {
align-items: last baseline;
grid-template-columns: subgrid;
grid-template-rows: masonry;
reading-order-items: grid rows;
}
.grid {
align-items: last baseline;
grid-template-columns: subgrid;
grid-template-rows: masonry;
reading-order-items: grid rows;
}
.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];
}
nav {
grid-area: primary-nav / fullbleed;
display: grid;
grid-template-columns: subgrid;
}
nav > .content {
grid-area: main;
}
Loading...
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
Loading...
Loading...
.masonry {
display: grid;
grid-template-columns:
repeat(auto-fill, minmax(14rem, 1fr));
grid-template-rows: masonry;
}
.masonry {
display: grid;
grid-template-columns: 1fr 2fr 3fr;
grid-template-rows: masonry;
}
@container (inline-size > 40em) {
.card {
flex-direction: column;
}
}
width
range
@media (20em < width <= 40em) {
...
}
.card-wrapper {
container-type: inline-size;
}
@container (inline-size > 40em) {
.card {
flex-direction: column;
}
}
.card-wrapper {
container-type: inline-size;
container-name: main;
}
@container main (inline-size > 40em) {
.card {
flex-direction: column;
}
}
.card-wrapper {
container: main / inline-size;
}
@container main (inline-size > 40em) {
.card {
flex-direction: column;
}
}
flere navne
.card-wrapper {
container: main / inline-size;
}
.card {
font-size: clamp(1.45rem, 6cqi, 1.75rem);
}
6 % af inline (bredden)
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
enables querying a parent's state
@container style(--theme: fun) {
}
@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;
}
@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;
}
<article style="--quote-type: highlight">
<div class="blockquote">
<blockquote>
Underline your fucking links you sociopaths.
</blockquote>
<p><em>Heydon Pickering</em></p>
</div>
</article>
: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ć
Manuel Matuzović
.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;
}
}
}
meget snart!
artcle:has(> img) {}
p:not(article > *) { ... }
article > :is(.class, #id) { ... }
:where(#article) a { ... }
article:has(h2) { ... }
article:not(:has(h2)) img { ... }
article:has(> :last-child:nth-child(3)) { ... }
:root:has(dialog[open]) {
overflow: hidden;
}
meget snart!
section {
border: 2px solid red;
color: red;
}
}
h2 {
section {
border: 2px solid red;
color: red;
}
}
&
h2 {
section {
border: 2px solid red;
color: red;
}
}
>
h2 {
section {
border: 2px solid red;
color: red;
}
}
:is( )
h2 {
✅
Hoisted
.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;
}
}
}
}
p:not(#id) {
color: green;
}
p.class {
color: red;
}
<p class="class">text</p>
<p id="id">last text</p>
0,0,0
0,0,1
@layer reset {
/* ... other reset rules */
input[type="text"] {
border: 1px solid gray;
}
}
@layer components {
/* ... other component rules */
.my-input {
border: 4px solid blue;
}
}
@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;
}
}
1
2
3
@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;
}
}
1
2
3
@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;
}
}
@layer reset, theme, global, layout, components, utilities, states;
p {
color: green;
}
#context {
color: red;
}
<div id="context">
<p>Some text</p>
</div>
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
@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>
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
.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);
}
.most-hyped {
--display-p3: color(display-p3 1 1 1);
--rec2020: color(rec2020 0 0 0);
}
.element {
background: oklch(80% 2 220);
}
lightness
chroma
hue
In OKLCH all backgrounds with L≥87% have good contrast with black text.
Centering stuff
.element {
display: flex;
gap: 1.25ch;
align-items: center;
}
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
giraf
Figma: Vertical trim
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.
h1 {
text-box-trim: both;
text-edge: cap alphabetic;
/* future? */
/* text-box-trim: cap alphabetic; */
}
article {
margin-trim: block;
}
Ikke mere * > * eller display: grid;
h1 {
text-wrap: balance;
}
p {
text-wrap: pretty;
}
article {
padding: 2rlh;
gap: 1rlh;
}
p:first-letter {
initial-letter: 3;
}
@font-face {
font-family: 'Rocher';
src: url('/fonts/RocherColorGX.woff2');
}
h1 {
font-family: "Rocher";
}
@font-face {
font-family: 'Rocher';
src: url('/fonts/RocherColorGX.woff2');
}
h1 {
font-family: "Rocher";
}
@font-palette-values --pink {
font-family: 'Rocher';
base-palette: 1;
}
h1 {
font-palette: --pink;
}
@font-palette-values --pink {
font-family: 'Rocher';
base-palette: 1;
}
h1 {
font-palette: --pink;
}
Intrinsic web
Hvorfor griber man typisk fat i JS?
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
Loading...
Loading...
Loading...
Tøjring af popovers
Loading...
Loading...
Loading...
<selectmenu>
<option value="r" checked>Red</option>
<option value="g">Green</option>
<option value="b">Blue</option>
</selectmenu>
Stylable dropdowns
selectmenu {
&:open { ... }
&::part(button) { ... }
&::part(marker) { ... }
> option::before { ... }
> option[value="r"]::before { background: red; }
}
Stylable dropdowns
built-in
selectmenu {
&:open { ... }
[slot="button"] { ... }
[behavior="selected-value"] { ... }
[slot="listbox"] { ... }
[popover] option { ... }
}
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>
slots & behavior
selectmenu {
&:open { ... }
[slot="button"] { ... }
[behavior="selected-value"] { ... }
[slot="listbox"] { ... }
[popover] option { ... }
}
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>
);
};
.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>
);
};
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
.box {
rotate: 45deg;
translate: -50% -50%;
scale: 1.1;
}
.box:hover {
rotate: 0deg;
}
@property --colorPrimary {
syntax: '<color>';
}
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
}
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
@property performance
Optimized composited scroll animations
Not blocking main thread
Loading...
Loading...
Loading...
Loading...
.element {
animation: rotate linear;
animation-timeline: scroll(block root);
}
.element {
animation: rotate linear;
animation-timeline: view(inline);
animation-range: entry;
}
Optimized flip-animations
Loading...
Loading...
Loading...
.box-1 {
view-transition-name: box-1;
}
.box-2 {
view-transition-name: box-2;
}
.box-1 {
view-transition-name: box-1;
}
.box-2 {
view-transition-name: box-2;
}
btn.addEventListener("click", () => {
document.startViewTransition(_ => grid.classList.toggle("col-count"));
});
.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");
}
});
.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;
}