Skip to content

Design system

A design system is a set of reusable decisions — colors, spacing, typography, component patterns — that keep your site consistent. CSS gives you the tools to build one, and the Style panel lets you apply it visually.

Two things to remember:

  • Classes are your building blocks. A well-named class can be reused across any element, any page.
  • CSS variables are your source of truth. Change a variable once, and every element using it updates.

Starting with CSS variables

Before styling anything, define your core values. Open the CSS Variables panel in the left sidebar and create variables for:

Colors: - --color-primary — your brand color - --color-secondary — accent color - --color-text — default text color - --color-background — page background - --color-surface — card/section backgrounds - --color-border — subtle borders

Spacing: - --space-xs4px - --space-sm8px - --space-md16px - --space-lg32px - --space-xl64px

Typography: - --font-heading — heading font family - --font-body — body text font family - --font-size-base — base text size (e.g., 16px) - --font-size-lg — large text - --font-size-sm — small text

Now every style you write references these variables instead of hard-coded values. Need to tweak the brand color? Change --color-primary once. See CSS variables for the full guide.

Naming classes with BEM

BEM (Block, Element, Modifier) gives you a predictable naming convention:

  • Block — a standalone component: .card, .nav, .hero
  • Element — a part of a block: .card__title, .card__image, .card__body
  • Modifier — a variation: .card--featured, .card--compact

In the Style panel, type your class name in the selector field. BEM names are long but self-documenting — .card__title--large tells you exactly what it styles and where.

Why BEM? - No naming collisions — .card__title won't accidentally style a title somewhere else - Easy to find — search for .card and you find everything related to cards - Works with Symbols — a Symbol with BEM classes stays consistent across instances

Building reusable component classes

A design system is not just variables — it's patterns. Here's how to build a reusable card component:

  1. Add a Div to the canvas — this is the card block
  2. Inside it: an Image (.card__image), a Heading (.card__title), a Paragraph (.card__body)
  3. Select the outer Div, add class .card
  4. Style .card:
  5. Background-color: var(--color-surface)
  6. Border-radius: 8px
  7. Padding: var(--space-md)
  8. Display: flex, Flex-direction: column, Row-gap: var(--space-sm)
  9. Style .card__title: Font-family var(--font-heading), Font-size var(--font-size-lg)
  10. Style .card__body: Color var(--color-text)

Now duplicate the card across pages. Every instance uses the same classes, the same variables. Change --color-surface and all cards update.

Utility classes for common patterns

Beyond components, create small utility classes for repeated patterns:

  • .text-centerText-align: center
  • .visually-hidden — accessible hiding (for screen readers)
  • .container — max-width with auto margins for centered page content
  • .flex-rowDisplay: flex, default row direction
  • .flex-col — Display: flex, Flex-direction: column
  • .gap-mdColumn-gap and Row-gap: var(--space-md)

Apply multiple classes to one element: select it, type the first class, then add another in the selector field. The element inherits styles from all its classes.

Combining Symbols and classes

Symbols handle structure (shared header, footer, nav). Classes handle styling. Together they form your design system:

  • Header Symbol with classes .site-header, .site-header__logo, .site-header__nav
  • Footer Symbol with classes .site-footer, .site-footer__links
  • Card pattern — not a Symbol (content differs per instance), but all share .card classes

Rule of thumb: if the structure and content are identical across pages, use a Symbol. If only the styling is shared, use classes.

Keeping things consistent across breakpoints

Your design system should work at every screen size. Two strategies:

  1. Variables that adapt. Define --space-lg as 32px on desktop, then override it to 16px at the mobile breakpoint. Every element using var(--space-lg) adapts automatically. See breakpoints.

  2. Component classes that flex. A .card grid that uses Flex-wrap with fixed-width cards adapts without any breakpoint-specific styles — cards just wrap to fewer columns. See Flexbox.

Practical example: design system for an agency site

Let's set up a system for the full site tutorial:

1. Define variables: - Colors: --color-primary: #2563eb, --color-text: #1e293b, --color-background: #ffffff, --color-surface: #f8fafc - Spacing: xs through xl (4px to 64px scale) - Fonts: --font-heading: 'Inter', sans-serif, --font-body: 'Inter', sans-serif

2. Create component classes: - .site-header — flex row, space-between, centered, padding var(--space-md) - .hero — flex column, centered, padding var(--space-xl), background var(--color-primary), white text - .card — surface background, rounded corners, flex column, gap - .section — padding var(--space-xl) top and bottom, max-width container

3. Convert shared elements to Symbols: - Header → Symbol (reused on every page) - Footer → Symbol

4. Test at every breakpoint: - Desktop: 3-column card grid, full hero - Tablet: 2-column cards, smaller hero padding - Mobile: 1-column cards, stacked nav

Common mistakes

  • Hard-coding colors and sizes. Every value should reference a CSS variable. If you type #2563eb directly, you'll have to find and replace it everywhere later.
  • Naming classes after appearance. .blue-box breaks when you rebrand. Name classes after purpose: .card, .highlight, .alert.
  • Too many one-off classes. If you have .hero-title-special-homepage, you're not reusing enough. Compose from existing classes instead.
  • Forgetting to test across breakpoints. A system that only works on desktop isn't a system.
  • Mixing Symbols and classes for the wrong use case. Symbols sync structure. Classes sync style. Don't make everything a Symbol.

Learn more


Quiz

Q1: You want to change your brand color across the entire site. What's the fastest way?

  • A) Find and replace the hex code in every class
  • B) Change the --color-primary CSS variable
  • C) Edit each element individually in the Style panel
Answer

B) Change the --color-primary CSS variable — every element using var(--color-primary) updates automatically. That's the whole point of defining variables first.

Q2: You have a card component used on 3 pages. The content is different on each page but the styling is the same. Should you use a Symbol?

  • A) Yes, Symbols keep styling consistent
  • B) No, use shared CSS classes instead
  • C) Use both a Symbol and classes
Answer

B) No, use shared CSS classes instead — Symbols sync structure and content. Since the content differs per page, classes are the right tool for shared styling.

Q3: What does the BEM class name .card__title--large tell you?

  • A) A card with a large title that is a separate component
  • B) The title element inside a card block, with a "large" variation
  • C) A title that is larger than a card
Answer

B) The title element inside a card block, with a "large" variationcard is the block, __title is the element, --large is the modifier.

Q4: Your spacing feels inconsistent across the site. What should you do?

  • A) Define spacing CSS variables (--space-sm, --space-md, etc.) and use them everywhere
  • B) Set the same pixel value on every element
  • C) Use the browser's default spacing
Answer

A) Define spacing CSS variables and use them everywhere — a spacing scale ensures consistency. When you need to adjust, change the variable once.

Q5: You want your card grid to automatically show fewer columns on mobile without writing breakpoint-specific styles. How?

  • A) Use CSS Grid with auto-fit
  • B) Use Flexbox with Flex-wrap and fixed-width cards
  • C) Create separate layouts for each breakpoint
Answer

B) Use Flexbox with Flex-wrap and fixed-width cards — cards wrap to the next row when there isn't enough space. No breakpoints needed for this basic responsive behavior.

Edit this page on GitLab