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):
- Reset - Browser normalisation (rarely used)
- Base - Design tokens and typography
- Components - Mixin features and reusable components
- Theme - Your site-specific customisations
- 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:
- Is your CSS wrapped in the correct
@layer? - Are you in a lower-priority layer trying to override a higher one?
- 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:
- CSS Architecture User Guide - Detailed patterns and best practices
- Component Development Guide - Building reusable components
Summary
Remember:
- Use layers - All CSS goes in
@layerblocks - Two main layers - Components for mixins, Theme for customisation
- Use variables -
var(--spacing-md)not16px - Prefix classes -
.blog-cardnot.card - Higher layers win - Theme overrides Components automatically
With these five principles, you can write maintainable, conflict-free CSS in MintyFlask.