Learn MintyFlask's five-layer CSS system in 5 minutes

CSS Architecture Quick Start

What You Need to Know

MintyFlask uses a five-layer CSS system that eliminates specificity wars. Styles in higher layers always win, regardless of selector complexity.

The five layers (lowest to highest priority):

  1. Reset - Browser normalisation (rarely used)
  2. Base - Design tokens and typography
  3. Components - Mixin features and reusable components
  4. Theme - Your site-specific customisations
  5. Utilities - Emergency overrides and helpers

90% of your work happens in just two layers: Components (for mixins) and Theme (for customisations).

The Key Concept

@layer base {
  .button {
    background: blue;
  }
}

@layer components {
  .button {
    background: green;
  } /* Overrides blue */
}

@layer theme {
  .button {
    background: red;
  } /* Overrides green */
}

/* Result: button is red - no specificity needed! */

Layer order determines precedence—not selector complexity, not source order, not !important.

Your Two Essential Layers

Components Layer: Building Reusable Styles

Use when: Creating mixin features, reusable components

/* mixins/blog/static/css/blog.css */

@layer components {
  /* Blog post card */
  .blog-post-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--spacing-lg);
  }

  /* Card title */
  .blog-post-title {
    font-size: var(--text-xl);
    font-weight: 600;
    margin-bottom: var(--spacing-sm);
  }

  /* Dark mode variant */
  .dark .blog-post-card {
    background: var(--color-surface-dark);
    border-color: var(--color-border-dark);
  }
}

Key points:

  • Wrap all mixin styles in @layer components
  • Use CSS custom properties (variables) instead of hardcoded values
  • Prefix class names to avoid conflicts (.blog-, .portfolio-, etc.)
  • Include dark mode variants

Theme Layer: Customising Your Site

Use when: Overriding mixin styles, adding theme-specific styles

/* themes/blog/static/css/custom.css */

@layer theme {
  /* Override blog card styling */
  .blog-post-card {
    border-radius: 0; /* Square corners for this theme */
    border-width: 2px; /* Thicker border */
  }

  /* Theme-specific component */
  .hero-banner {
    background: linear-gradient(
      to right,
      var(--color-primary),
      var(--color-secondary)
    );
    padding: var(--spacing-xl) 0;
  }
}

Key points:

  • Wrap theme customisations in @layer theme
  • Override only what you need to change
  • Add theme-specific components here
  • Still use CSS custom properties

Quick Decision Guide

Where should this CSS go?

Are you creating a reusable mixin component?
├─ YES → Components layer
└─ NO → Are you customising for your specific site?
    ├─ YES → Theme layer
    └─ NO → Check the other layers (base/utilities)

The Other Three Layers (Brief)

Base Layer: Foundation

Use when: Setting up design tokens, default element styles

@layer base {
  :root {
    /* Design tokens */
    --color-primary: oklch(60% 0.15 250);
    --spacing-md: 1rem;
    --text-xl: 1.25rem;
  }

  /* Default element styling */
  body {
    font-family: system-ui, sans-serif;
    color: var(--color-text-primary);
  }

  h1 {
    font-size: var(--text-xl);
  }
}

Utilities Layer: High Priority

Use when: Accessibility helpers, debugging, print styles

@layer utilities {
  /* Screen reader only */
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
  }

  /* Print-specific */
  @media print {
    .no-print {
      display: none;
    }
  }
}

Reset Layer: Browser Normalisation

Use when: Almost never—Tailwind handles this

@layer reset {
  /* Rare: only if you need specific resets */
  * {
    margin: 0;
    padding: 0;
  }
}

Common Patterns

Pattern 1: Creating a Mixin Component

/* mixins/portfolio/static/css/portfolio.css */

@layer components {
  /* Use mixin prefix */
  .portfolio-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--spacing-lg);
  }

  .portfolio-project-card {
    background: var(--color-surface);
    border-radius: var(--radius-md);
    overflow: hidden;
    transition: transform 0.2s ease;
  }

  .portfolio-project-card:hover {
    transform: translateY(-4px);
  }

  /* Dark mode */
  .dark .portfolio-project-card {
    background: var(--color-surface-dark);
  }
}

Pattern 2: Customising in Your Theme

/* themes/agency/static/css/custom.css */

@layer theme {
  /* Override portfolio card for agency theme */
  .portfolio-project-card {
    border: 2px solid var(--color-accent);
    border-radius: 0; /* Square corners */
  }

  .portfolio-project-card:hover {
    transform: scale(1.05); /* Different hover effect */
    border-color: var(--color-primary);
  }
}

Pattern 3: Using CSS Custom Properties

/* Base layer sets defaults */
@layer base {
  :root {
    --button-padding: 0.5rem 1rem;
    --button-radius: 0.375rem;
  }
}

/* Theme overrides for larger buttons */
@layer theme {
  :root {
    --button-padding: 0.75rem 1.5rem;
    --button-radius: 0.5rem;
  }
}

/* Component uses the custom properties */
@layer components {
  .btn-base {
    padding: var(--button-padding);
    border-radius: var(--button-radius);
  }
}

Common Mistakes

✗ Wrong: Unlayered CSS

/* This CSS has unpredictable priority */
.blog-post-card {
  background: red;
}

✓ Right: Layered CSS

@layer components {
  .blog-post-card {
    background: red;
  }
}

✗ Wrong: Hardcoded Values

@layer components {
  .blog-post-card {
    padding: 16px;
    background: #3b82f6;
  }
}

✓ Right: Using Variables

@layer components {
  .blog-post-card {
    padding: var(--spacing-md);
    background: var(--color-primary);
  }
}

✗ Wrong: Generic Class Names

@layer components {
  /* Conflicts with other mixins! */
  .card {
  }
  .grid {
  }
}

✓ Right: Prefixed Names

@layer components {
  /* Clear ownership */
  .blog-card {
  }
  .blog-grid {
  }
}

File Organisation

Where do CSS files go?

mixins/blog/static/css/
└── blog.css              # @layer components

themes/blog/static/css/
├── src/
│   └── input.css         # Imports everything
└── dist/
    └── output.css        # Built CSS

themes/_core/static/css/
├── foundations/
│   ├── tokens.css        # @layer base (design tokens)
│   └── typography.css    # @layer base (element styles)
├── components/
│   └── buttons.css       # @layer components (core)
└── utilities/
    └── helpers.css       # @layer utilities

Your First Custom Style

Let's add a custom hero banner to your theme:

/* themes/blog/static/css/src/custom.css */

@layer theme {
  /* Hero banner component */
  .hero-banner {
    background: linear-gradient(
      135deg,
      var(--color-primary),
      var(--color-secondary)
    );
    color: white;
    padding: var(--spacing-xl) var(--spacing-lg);
    text-align: centre;
    border-radius: var(--radius-lg);
  }

  .hero-banner-title {
    font-size: var(--text-3xl);
    font-weight: 700;
    margin-bottom: var(--spacing-md);
  }

  .hero-banner-subtitle {
    font-size: var(--text-lg);
    opacity: 0.9;
  }
}

In your template:

<div class="hero-banner">
  <h1 class="hero-banner-title">Welcome to My Site</h1>
  <p class="hero-banner-subtitle">Building with MintyFlask</p>
</div>

Troubleshooting

My styles aren't applying

Check:

  1. Is your CSS wrapped in the correct @layer?
  2. Are you in a lower-priority layer trying to override a higher one?
  3. Did you import your CSS file in input.css?

Solution:

/* Wrong: components can't override theme */
@layer components {
  .my-override {
    color: red;
  }
}

/* Right: use higher layer */
@layer theme {
  .my-override {
    color: red;
  }
}

Dark mode not working

Check: Dark mode selector should be .dark class:

/* Wrong */
@media (prefers-color-scheme: dark) {
  .card {
    background: black;
  }
}

/* Right */
@layer components {
  .card {
    background: white;
  }

  .dark .card {
    background: black;
  }
}

Next Steps

You now know enough to write CSS in MintyFlask. For more details:

Summary

Remember:

  1. Use layers - All CSS goes in @layer blocks
  2. Two main layers - Components for mixins, Theme for customisation
  3. Use variables - var(--spacing-md) not 16px
  4. Prefix classes - .blog-card not .card
  5. Higher layers win - Theme overrides Components automatically

With these five principles, you can write maintainable, conflict-free CSS in MintyFlask.