CRAFTING UI

Frontend Design

The fundamentals

Overblik og recap

Agenda

The Cascade

Selectors

Nesting

The Cascade

Selectors

Agenda

  1. Blive bedre til CSS

  2. Forstå, hvordan Nesting hjælper med organisering

  3. Blive bedre til at bruge 

  4. Øvelser, øvelser og flere øvelser for at opbygge praktisk erfaring

Nesting

Selectors

Nesting

  1. Forstå                            bedre

  2. Blive bedre til CSS                     og udvide kendskabet

  3. Forstå, hvordan                  hjælper med organisering

  4. Øvelser for at opbygge praktisk erfaring

The Cascade

Agenda

Agenda

The Cascade

Reset

Mål: Forstå the cascade bedre

Inheritance

Specificity

Cascade Layers

... og venner

<!DOCTYPE html>
<html lang="da">

  <head>...</head>

  <body>
    
    <h1>Heading 1</h1>
    
    <main>
      
      <h2>Heading 2</h2>
      <p>Lorem ipsum dolor sit.</p>
      
      <section>
        
        <h3>Hello</h3>
        <p>Lorem ipsum dolor sit amet consectetur.</p>
      
      </section>
    
    </main>

  </body>
  
</html>
main {
  color: green;
  font-size: 1.1rem;
  border: 1px solid blue;
}

section {
  border: inherit;
}

section p {
  color: red;
  font-size: 1rem;
}

Inheritance

Inheritance

Properties, der nedarves:

  • color

  • font-family

  • font-size

  • font-weight

  • line-height

  • list-style

  • text-align

  • m.fl.

html {
  color: gray;
  line-height: 1.5;
  font-family: system-ui, sans-serif;
}

h1,
h2,
h3 {
  color: #000;
  line-height: 1.1;
  font-family: "My Display Font";
}

Standard styles

Overskrivning

God praksis

* {
	margin: 0;
	box-sizing: border-box;
}

img {
	display: block;
	max-width: 100%;
	height: auto;
}

Hvorfor gør vi det?

God praksis

html {
	display: block;
}
head {
	display: none;
}
body {
  display: block;
  margin: 8px;
}
h1 {
  display: block;
  font-size: 2em;
  margin-block-start: 0.67em;
  margin-block-end: 0.67em;
  font-weight: bold;
}
a:-webkit-any-link {
  color: -webkit-link;
  cursor: pointer;
  text-decoration: underline;
}

Defaults

User Agent Stylesheet

En masse regler er defineret, inden du selv har skrevet én linje CSS

Browser

(

)

Defaults

.example {
  display: flex;
  
}

.example > .child {

}

Defaults

.example {
  display: flex;
  align-items: stretch;
  justify-content: normal;
  align-content: normal;
  align-items: normal;
  flex-direction: row;
  flex-wrap: nowrap;
}

.example > .child {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
}

Defaults

Hvorfor sker der ikke noget?

DevTools har ofte svaret

Det perfekte diagnosticeringsværktøj

CSS Reset

Et bedre udgangspunkt

CSS Reset

Et bedre udgangspunkt

"Starter"

starter.css

body {
    margin: unset;
    min-height: 100svh;
}
img {
	vertical-align: middle;
	max-width: 100%;
	height: auto;
}
html {
  font-family: system-ui;
  line-height: 1.5;
  color: #3b3b3b;
}
* {
	margin: 0;
	box-sizing: border-box;
}

starter.css

body {
  min-height: 100svh /* 100dvh */;
}

...
* {
	margin: 0;
	box-sizing: border-box;
}

img {
	display: block;
	max-width: 100%;
	height: auto;
}
:not(progress, meter) {
  border-style: solid;
  border-width: 0;
}

Temaets reset

Cascade Layers

Mål: Forstå principperne bag

Cascade Layers

Visuel ensretning

Jeres tur

The cascade

  1. Tilføj en blå border på 4px til .my-input
  2. Kig i DevTools for at finde fejlen
  3. Lad den være åben

The cascade

Author styles

0,0,0

0,0,1

🤷‍♂️

Cascade layers

Layer A

Layer B

Layer C

Cascade layers

Layer A

Layer B

Layer C

input[type="text"] {
  border: 1px solid gray;
}
.my-input {
  border: 4px solid blue;
}

Højeste prioritet

Laveste prioritet

Cascade layers

Layer A

Layer B

Layer C

input[type="text"] {
  border: 1px solid gray;
}
.my-input {
  border: 4px solid blue;
}

Højeste prioritet

Laveste prioritet

Cascade layers

Layer A

Layer B

Layer C

input[type="text"] {
  border: 1px solid gray;
}
.my-input {
  border: 4px solid blue;
}

Højeste prioritet

Laveste prioritet

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;
  }
}
input[type="text"] {
  border: 1px solid gray;
}
.my-input {
  border: 4px solid blue;
}

Layer A

Layer B

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

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

1

2

3

Cascade layers

@layer reset, global, components, utilities;

Cascade layers

@layer reset, global, components, utilities;

Hvert projekt

Projektspecifikt?

Projektspecifikt!

One-offs, hvert projekt?

Cascade layers

@layer reset, global, components, utilities;

@layer reset {
  ul[class] {
    margin: 0;
    padding: 0;
    list-style: none;
    text-align: start;
  }
}

@layer utilities {
  .flow {
    > * + * {
    	margin-top: 1em;  
    }
  }
}

eksempel

Cascade layers

øvelse

ul[class] {
  margin: 0;
  padding: 0;
  list-style: none;
}

Cascade layers

øvelse

.flow {
  > * + * {
    margin-top: 1rlh;
  }
}

Cascade layers

øvelse

ul[class] {
  margin: 0;
  padding: 0;
  list-style: none;
}

.flow {
  > * + * {
    margin-top: 1rlh;
  }
}

0,1,1

0,1,0

Cascade layers

øvelse

@layer reset, global, components, utils;

Cascade layers

øvelse

  1. Oversæt selectoren (ItsLearning)

  2. Refleksion i plenum

Øvelser

Cascade Layers

Mål: Blive bedre og udvide kendskabet samt vurdere kvalitet og vedligeholdelse

:has()

Mål: Blive bedre og udvide kendskabet

Cascade Layers

artcle:has(img) {}

p:not(.lede) { ... }

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

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

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

:has()

article:has(h2) { ... }

article:has(h2, h3) { ... }

article:has(h2):has(h3) { ... }

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

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

:has()

Hvad styler vi her?

:has()

body {
  display: grid;
}

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

Playground

:has()

"Kom i gang"-øvelser

:not(:has(img))

i kombination med :not()

:has()

combinators

(mellemrum)
+
~
>

:has()

combinators

p + figure {
  ...
}

figure ~ p {
  ...
}
<div>
  <p>Hello</p>
  <figure></figure>
  <h2>Hello</h2>
</div>

<div>
  <p>Hello</p>
  <h2>Hello</h2>
  <figure></figure>
  <p>Hello</p>
</div>

:has()

combinators

p:has(+ figure) {
  ...
}

figure:has(~ p) {
  ...
}
<div>
  <p>Hello</p>
  <figure></figure>
  <h2>Hello</h2>
</div>

<div>
  <p>Hello</p>
  <h2>Hello</h2>
  <figure></figure>
  <p>Hello</p>
</div>

:has()

display: contents?

:has()

display: contents;

p:has(img) {
  display: contents;
}
<article>
  

    <img>

  
</article>
<p>
</p>

:has()

display: contents;

p:has(img) {
  display: contents;
}
<article>
  
  
    <img>
  
  
</article>
<p>
</p>

:has()

display: contents;

p:has(img) {
  display: contents;
}
<article>
  

    <img>

  
</article>
<p>
</p>

Opfører sig nu som barn af <article>

:has()

:has()

quantity queries

ul:not(:has(> li:nth-child(5))) {
  display: flex;
  justify-content: center;
}

:has()

:has()

:has()

:has()

Nesting

Mål: Forstå, hvordan det hjælper med organisering

Nesting 

section {
  border: 2px solid red;
}
 
section 



  color: red;
}
h2 {

Nesting

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

  color: red;
}

Nesting

section {
  border: 2px solid red;
  
  
    color: red;
  }
}
>
h2 {
<section>
  <h2></h2>
  <h3></h3>
  <p></p>
  <article>
    <h2></h2>
  </article>
</section>

Nesting

section {
  border: 2px solid red;
  
  
    color: red;
  }
}
>
h2 {
<section>
  <h2></h2>
  <h3></h3>
  <p></p>
  <article>
    <h2></h2>
  </article>
</section>

Nesting

section {
  border: 2px solid red;
  
  
    color: red;
  }
}
h2, h3 {
<section>
  <h2></h2>
  <h3></h3>
  <p></p>
  <article>
    <h2></h2>
  </article>
</section>

Nesting



:is()
:where()
:nth-child()
.
>
+
~
[data-*]
&

Symboler

Nesting

h1,
h2 {  
  & span {
    color: blue;
  }
}
& === :is(h1, h2)

Nesting

h1 {
  color: red;
  
  @media (width > 500px) {
    color: blue;
  }
}

Nesting

h1 {
  @media (width > 500px) {
    color: blue;
  }
  
  color: red;
}

Nesting

h1 {
  article & {
    color: red;
  }
}

Nesting

h1 {
  color: blue;
  
  article & {
    color: red;
  }
}

if / else

<h1>I'm blue</h1>

<div class="parent">
  <h1>I'm red</h1>
</div>

Nesting

h1 {
  color: blue;
  
  article & {
    color: red;
  }
}

if / else

<h1>I'm blue</h1>

<article>
  <h1>I'm red</h1>
</article>

Nesting

h2 {
  color: blue;
  
  section :nth-last-child(2 of &) {
    color: red;
  }
}

Please don't

Nesting

a {
  background: red;
  
  &:hover {
    background: blue;
  }
}

Co-location

Please do

Nesting

.btn {
  padding: 0.5rem;

  &:is(:hover, :focus-visible) {
    background: blue;
  }
}

Co-location

Please do

Nesting

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

Co-location

Please do

Nesting

Omskriv til nesting

Nesting

Al logik skal være i article

article {
  /* nesting logik */
}
section:nth-child(...) {
  article {
    
  }
}

🚫

Nesting

Al logik skal være i .article

Slides

Kursusmateriale

Crafting UI: The Fundamentals 2026

By Dannie Vinther

Crafting UI: The Fundamentals 2026

Crafting UI with css

  • 15