CRAFTING UI

Frontend Design
Custom by Design
Custom properties, style queries & custom commands



Agenda
Custom Properties
Style Queries
Custom Commands
Agenda
-
Forstå bedre
-
Forstå, hvordan man opbygger robuste layouts med
-
Kan -funktioner hjælpe med layout?
-
Øvelser for at opbygge praktisk erfaring
Custom Properties
Style Queries
Custom Commands
Custom Properties
Custom Properties
- Tokens: Genbrug farver, afstande og skriftstørrelser, så designet bliver ensartet.
- House props: Giv variabler navne efter formål, fx
--brandog--card-padding. - Adaptive props: Skift variabelværdier til dark mode eller forskellige skærmstørrelser via media queries.
- Lokale komponentvariabler (pseudo-private props): Saml en knaps eller et korts værdier lokalt, og tilpas dem ved fx
:hover.--_er kun en navnekonvention. - Partial props: Brug variabler som dele af en værdi, fx en fælles farvetone i flere
hsl()-farver eller en størrelse icalc().
.card {
padding: 1rem;
@container (width > 30ch) {
padding: 2rem;
}
}Smart?
.card {
padding: 1rem;
@container (width > 30ch) {
padding: 2rem;
}
h2 {
font-size: 1.5rem;
@container (width > 30ch) {
font-size: 2.5rem;
}
}
.card-actions {
@container (width > 30ch) {
flex-direction: row;
}
}
}Er det en god ide?
.card {
padding: 1rem;
h2 {
font-size: 1.5rem;
}
@container (width > 30ch) {
.card {
padding: 2rem;
}
h2 {
font-size: 2.5rem;
}
.card-actions {
flex-direction: row;
}
}
}Er det en god ide?
Custom Properties
html {
--primary: #5000ca;
--secondary: #463866;
--accent: #fb28a8;
--space-xxs: 0.25rem;
--space-xs: 0.5rem;
--space-sm: 1rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
--space-xxl: 6rem;
}eksempel
"Consistency"
Custom Properties
html {
--space-sm: 1rlh; /* eller flydende */
--space-lg: 2rlh; /* eller flydende */
}
.flow {
> * + * {
margin-top: var(--flow-space, var(--space-sm));
}
h2 {
--flow-space: var(--space-lg);
}
}Flow-space
var(--value, 10px)Custom Properties
Default værdi
.grid {
display: grid;
grid-template-columns:
repeat(var(--cols, 3), 1fr);
}
@media (width > 40em) {
.grid {
--cols: 5;
}
}Custom Properties
Default værdi
.object-1 {
--move-to: 100px;
}
.object-2 {
--move-to: 400px;
}
[class|="object"] {
animation: move 1s linear forwards;
}
@keyframes move {
to {
translate: var(--move-to);
}
}Custom Properties
SVG through data URL
:root {
--svg: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">[SVG PATH]</svg>');
}
Custom Properties

Tema-opgaven
Custom Properties
dry
Loading...
Custom Properties
dry
Loading...
Custom Properties
Custom Properties
Boolean-ish
.rotate {
--intent: 0;
rotate: /* rotér v/ hover */ ;
&:hover {
--intent: 1;
}
}Brug calc() til at gange variablen med 45deg
0 * 45deg = 0deg
1 * 45deg = 45deg
Custom Properties
Boolean-ish
.slide-out {
--open: 0;
opacity: var(--open);
translate: calc( var(--open) * 100% );
transition:
opacity .4s,
translate .4s;
&.open {
--open: 1;
}
}Brug calc() til at gange variablen med 100%
Custom Properties
Boolean-ish
Linear Mapping
.element {
--range: calc(0.6em * var(--p) + 0.2em);
/*
If --p is 0 --range is .2em
If --p is 1 --range .8em
*/
}Linear Mapping
Brug lineær mapping til at animere en variabel font ved musens bevægelse
Linear Mapping
Øvelse
p {
color: green;
}
#context {
color: red;
}<div id="context">
<p>Some text</p>
</div>Kontekst
Kontekst
:root {
--my-color: green;
}
aside {
--my-color: red;
}
p {
color: var(--my-color);
}aside p {
--my-color: red;
}
p {
color: var(--my-color, green);
}Kontekst
:root {
--my-color: green;
}
aside {
--my-color: red;
}
p {
color: var(--my-color);
}aside {
--my-color: red;
}
p {
color: var(--my-color, green);
}Kontekst
@container
Container queries
Spørg containeren
@container
Container queries
Spørg containeren
- size
- name
- style
- scroll-state
Size queries
Style queries
Style queries
Style queries
Style queries
Style queries
Tema-opgaven
Kontekst


Kontekst
[data-surface] {
--surface: attr(data-surface type(dark | light));
}
[data-surface="dark"] {
--surface-bg: var(--color-neutral-900);
}
.card {
--card-bg: var(--color-neutral-700);
@container style(--surface: dark) {
--card-bg: var(--color-neutral-100);
}
}<section data-surface="dark">
<!-- content -->
</section>style queries

Kontekst
[data-surface] {
--surface: attr(data-surface type(dark | light));
}
[data-surface="dark"] {
--surface-bg: var(--color-neutral-900);
}
.card {
--card-bg: var(--color-neutral-700);
@container style(--surface: dark) {
--card-bg: var(--color-neutral-100);
}
}<section data-surface="dark">
<!-- content -->
</section>style queries

Brug custom properties og style queries til lettere at organisere hver sektions farvetema samt indhold.
Themed sections
Øvelse

Lav øvelsen, der ligger på git-branch'en "scrolling-container" på GitHub
Scrolling Container
Øvelse
- Scroll-snap
- Invoker commands
Opskrift:
Scroll-snap
.scroll-container {
scroll-snap-type: inline mandatory; /* block, proximity */
overflow-inline: auto; /* scroll */
}
.item {
scroll-snap-align: center; /* start, end */
}Scroll-snap
Scroll-snap
Demo
Invoker Commands
Deklarativ interaktion
<button
command="toggle-popover"
commandfor="popover-1">
Open Popover
</button>
<div popover id="popover-1">
<!-- indhold -->
</div>
Invoker Commands
Invoker Commands
Custom commands
Invoker Commands
Custom commands
Invoker Commands
Custom commands
Invoker Commands
Custom commands
const scroller = document.querySelector("#scroller");
scroller.addEventListener("command", (e) => {
if (e.command === "--scroll") {
scroller.scrollBy({
left: scroller.offsetWidth * e.source.value
})
}
});<button
value="-.9"
command="--scroll"
commandfor="scroller">
Prev
</button>Invoker Commands
Custom commands
const scroller = document.querySelector("#scroller");
scroller.addEventListener("command", (e) => {
if (e.command === "--scroll") {
scroller.scrollBy({
left: scroller.offsetWidth * e.source.value
})
}
});<button
value="-.9"
command="--scroll"
commandfor="scroller">
Prev
</button>Disabled buttons
disabled
function disableButtons() {
const maxScroll = scroller.scrollWidth - scroller.offsetWidth;
prev.disabled = scroller.scrollLeft <= 0;
next.disabled = scroller.scrollLeft >= maxScroll - 1;
}
scroller.addEventListener("scroll", disableButtons);
disableButtons()Hele bredden
Den synlige bredde
boolean
boolean
pattern
max()

:root {
--content-width: 75rem;
}
main {
grid-template-columns:
1fr minmax(0, var(--content-width)) 1fr;
}
.scroller {
padding-inline:
max(1rem, 50% - var(--content-width) / 2);
}
pattern
max()

section {
padding-inline:
max(1rem, 50% - var(--content-width) / 2);
}Lav øvelsen, der ligger på git-branch'en "scrolling-container" på GitHub
Scrolling Container
Øvelse
NB! Du skal selv sætte knapperne ind
Crafting UI: Custom By Design
By Dannie Vinther
Crafting UI: Custom By Design
- 155