CRAFTING UI
Frontend Design
Datavisualisering og UI-grafik
Det handler ikke kun om at vise data men om at designe den måde, data forstås på.
Vælg form efter budskab
Data før grafik. Grafikken er et ekstra lag.
<figure>
<figcaption>Erfaring pr. år</figcaption>
<ul>
<li>2023: 14 %</li>
<li>2024: 26 %</li>
<li>2025: 69 %</li>
</ul>
</figure>Hvis grafen forsvinder, skal data stadig kunne forstås
Vores værktøjkasse
Typed attr()
Farvemanipulation
shape()
Gradienter
offset-path
<path>
<circle>
pathLength
stroke-dasharray
stroke-dashoffset
border-area
Pseudo-elementer
color-mix() & Relative Color Syntax
hover- og active-state
a::after {
content: '(opens in new tab)';
}::before, ::after
a[href^="http"]::after {
content: '(opens in new tab)';
}::before, ::after
::before, ::after
a[href^="http"]::after {
content: '(opens in new tab)';
}Øvelse
nav.innerHTML += `<a href=${c.link}>${c.name}</a>` + " / ";Øvelse
Øvelse
li + li::before {
content: '/';
}Øvelse
li + li::before {
content: '/';
}li:has(a)::after {
content: '/';
}li:has(+ li)::after {
content: '/';
}li:not(:last-child)::after {
content: '/';
}:not(:first-child)::before {
content: '/';
}Fra temaopgaven
Fra streng til datatype
[data-color]::after {
content: attr(data-color);
}
<h2 data-color="red"></h2>
Indsætter strengen "red"
Fra streng til datatype
[data-color] {
color: attr(data-color type(<color>));
}
Sætter tekstfarven
Fra streng til datatype
[data-color] {
color: attr(data-color type(<color>), #fff);
}
fallback
Fra streng til datatype
[data-color] {
color: attr(data-color type(<color>), #fff);
}
<h2 data-color="red">
Make me red
</h2>
Fra streng til datatype
[data-color] {
color: attr(data-color type(<color>), #fff);
}
<h2 data-color="oklch(0.6 0.3 29)">
Make me red
</h2>
Fra streng til datatype
klik her
<div data-length="5rem"></div>div {
width: attr(data-length type(<length>));
&::after {
content: attr(data-length);
}
}Units vs. Types
Units vs. Types
<angle>
<color>
<custom-ident>
<image>
<integer>
<length>
<length-percentage>
<number>
<percentage>
<time>
Types of type()
article {
display: grid;
grid-template-columns:
repeat(8, 1fr);
height: 300px;
}
div:nth-child(1) {
height: 100%;
}
div:nth-child(2) {
height: 70%;
}
...<article>
<div></div>
<div></div>
...
</article>Ikke så fleksibelt
10%20%30%40%50%60%70%100%article {
display: grid;
grid-template-columns:
repeat(8, 1fr);
}
div:nth-child(1) {
height: 100%;
}
div:nth-child(2) {
height: 70%;
}
...<article>
<div style="height: 100%"></div>
<div style="height: 70%"></div>
...
</article>Data kommer typisk fra JS
10%20%30%40%50%60%70%100%bar.style.height = `${percent}%`div {
height: var(--height, 0%);
}<article>
<div style="--height: 100%"></div>
<div style="--height: 70%"></div>
...
</article>Det kunne ligeså godt se sådan ud
10%20%30%40%50%60%70%100%bar.style.setProperty("--height", `${percent}%`)<article>
<div data-percent="100"></div>
<div data-percent="70"></div>
...
</article>Eller sådan her
(en moderne løsning)
div {
height: attr(data-percent %, 0%);
}10%20%30%40%50%60%70%100%bar.dataset.percent = percentdiv {
width: attr(data-length type(<length>));
&::after {
content: attr(data-length);
}
}attr(data-number type(<number>), 0)
div {
height: attr(data-percent %), 0%);
}.bars {
display: grid;
grid-template-columns: repeat(8, 1fr);
height: 300px;
}Basic grid
<article class="bars">
<div class="bar" data-percent="100"></div>
<div class="bar" data-percent="70"></div>
<!-- ... -->.bar {
height: attr(data-percent %); /* 70% */
}Sæt højden i %
<article class="bars">
<div class="bar" data-percent="100"></div>
<div class="bar" data-percent="70"></div>
<!-- ... -->.bar {
height: attr(data-percent %, 0%); /* 0% */
}Husk fallback!
<article class="bars">
<div class="bar" data-percent="100"></div>
<div class="bar"></div>
<!-- ... -->.bar {
background:
color-mix(#3980DD attr(data-percent %, 0%), #DAE9FC);
}Procent-værdien styrer baggrundsfarven
.bar {
--percent: attr(data-percent %, 0%);
background:
color-mix(#3980DD var(--percent), #DAE9FC);
height: var(--percent);
}Organiser med variabler
.bar {
&::after {
content: attr(data-percent) "%";
position: absolute;
top: -1lh;
inset-inline: 0;
justify-self: center;
}
}Vis procent-værdien
.bars {
@container (width < 400px) {
writing-mode: sideways-rl;
height: 100%;
}
}
.bar {
min-width: 40px;
}Gør den responsiv
div {
width: attr(data-length type(<length>));
&::after {
content: attr(data-length);
}
}writing-mode: sideways-lr | sideways-rl etc.
med flow-relative properties
med flow-relative properties
width
max-width
height
max-height
margin-left
margin-right
padding-top
top
inline-size
max-inline-size
block-size
max-block-size
margin-inline-start
margin-inline-end
padding-block-start
inset-block-start
med flow-relative properties
.bars {
@container (width < 400px) {
writing-mode: sideways-rl;
height: 100%;
}
}
.bar {
min-width: 40px;
}Gør den responsiv
.bars {
block-size: 300px; /* erstatter height */
@container (width < 400px) {
writing-mode: sideways-rl;
block-size: 100%; /* erstatter height */
}
}
.bar {
min-inline-size: 40px; /* erstatter min-width */
&::after {
inset-block-start: -1lh; /* erstatter top */
}
}Gør boksmodellen flow-relativ
.bars {
@container (width < calc((40px + 1rlh) * 8)) {
/* ... */
}
}
.bar {
margin-inline: .5rlh;
min-inline-size: 40px;
}Brug calc() i @container
bar width
.bars {
background:
repeating-linear-gradient(
to bottom,
gainsboro 0 1px,
transparent 0 25%
);
}Linjer med repeating-gradient
.bars {
background:
repeating-linear-gradient(
var(--angle, to bottom),
gainsboro 0 1px,
transparent 0 25%
);
}Fix linjer på små skærme
.bars {
background:
repeating-linear-gradient(
var(--angle, to bottom),
gainsboro 0 1px,
transparent 0 25%
);
@container (inline-size < 400px) {
--angle: to left;
}
}Fix linjer ved breakpointet
.bars {
background:
repeating-linear-gradient(
to bottom,
gainsboro 0 1px,
transparent 0 25%
);
}to bottom
0
1px
25%
(180deg)
Koniske, lineære, radiale
Koniske, lineære, radiale
.gradient-progress {
background: linear-gradient(
90deg,
[FARVE 1] [PROCENT],
[FARVE 2] 0
);
}
linear-gradient progress
mask
Fra temaopgaven
Fra temaopgaven
SVG
Fra bokse og gradients til koordinater, former og strokes.
viewBox giver os et stabilt koordinatsystem
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50" />
</svg>
Vi tegner en circle med koordinaterne: centerpunkterne for x og y: 50, 50
radius: 50
x og y
cx og cy
stroke
stroke-width
stroke-dasharray
stroke-dashoffset
stroke-linecap
fill
pathLength
.parent {
.child {
offset-path: border-box;
}
}svg {
circle {
offset-path: circle([COORDINATES]);
}
}