Design Systems That Actually Ship
Why most design systems fail and what practitioners can do about it.
Living style guide for mitoware.com. Every specimen below uses the production stylesheet. When blog.css changes, this page changes with it.
Thoughts on UX, AI, and Design
MitoWare is the Mitoware company blog. Articles are sourced from Directus CMS, rendered through Flask/Jinja, and served via Docker on Gogli. The design is intentionally restrained; typography and whitespace do the heavy lifting. Two fonts carry the brand: Inter (UI, headings) and Merriweather (long-form prose).
--color-primary: #4361ee
--color-primary-dark: #3a56d4
--color-accent: #0891b2
--color-bg: #ffffff
--color-bg-alt: #f8f9fa
--color-border: #e9ecef
--color-text: #1a1a2e
--color-text-muted: #6c757d
Used on image placeholders when no featured image exists.
135deg, primary → accent
The quick brown fox jumps over the lazy dog. 0123456789
The quick brown fox jumps over the lazy dog. 0123456789
The quick brown fox jumps over the lazy dog. 0123456789
Article Title
Featured Title
Section Title
Logo / List Title
Card Title
Navigation Link
February 10, 2026
Long-form article body text uses Merriweather for comfortable reading at sustained lengths. Line height is generous at 1.8 to give each line room to breathe.
/* Shadows */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
Used in the articles toolbar to switch between list and card views.
<div class="view-toggle"> <button type="button" class="view-btn"> <!-- SVG list icon --> </button> <button type="button" class="view-btn active"> <!-- SVG grid icon --> </button> </div>
<a href="..." class="read-more">Read Article →</a> <a href="..." class="view-all">View All Articles →</a> <a href="..." class="back-link">← Back to Articles</a>
3-column grid on desktop, 2 on tablet, 1 on mobile. Cards lift on hover with shadow-lg and translateY(-2px).
Why most design systems fail and what practitioners can do about it.
<article class="article-card"> <a href="..." class="card-image"> <img src="..." alt="..."> </a> <div class="card-content"> <span class="article-hub article-hub-small">HUB</span> <h3 class="card-title"> <a href="...">Title</a> </h3> <p class="card-excerpt">Excerpt...</p> <time class="card-date">Date</time> </div> </article>
When no featured image exists, a gradient placeholder (primary → accent) fills the image area.
This card uses the gradient placeholder.
Used in the "More Articles" section on article pages. No image, tighter padding.
<article class="article-card article-card-small"> <div class="card-content"> <span class="article-hub article-hub-small">HUB</span> <h3 class="card-title"> <a href="...">Title</a> </h3> <time class="card-date">Date</time> </div> </article>
Horizontal layout with 200px image on the left. Used in the articles archive when list view is active.
When users interact with AI-powered features, trust is the first hurdle. Here are practical patterns that work.
<article class="article-list-item"> <a href="..." class="list-item-image"> <img src="..." alt="..."> </a> <div class="list-item-content"> <span class="article-hub article-hub-small">HUB</span> <h2 class="list-item-title"> <a href="...">Title</a> </h2> <p class="list-item-excerpt">Excerpt...</p> <div class="list-item-meta"> <time>Date</time> <div class="article-tags"> <span class="tag">Tag</span> </div> </div> </div> </article>
Used for sort and per-page controls in the articles archive.
<div class="toolbar-control"> <label for="sort-select">Sort:</label> <select id="sort-select" class="toolbar-select"> <option value="newest">Newest First</option> <option value="oldest">Oldest First</option> </select> </div>
Complete toolbar as it appears on the articles archive page.
Used on home page (recent articles) and archive page (card view).
grid-template-columns: repeat(3, 1fr) · gap: var(--space-xl)
Image left, content right. Collapses to single column at 1024px.
grid-template-columns: 1fr 1fr · gap: var(--space-2xl)
1024px
Featured grid stacks. Article grid → 2 columns.
768px
Article grid → 1 column. List items stack. Toolbar wraps.
480px
Tighter padding. Toolbar stacks vertically. Prose → 1rem.
The .prose class wraps all article body content. It switches to Merriweather serif, increases font size to 1.125rem, and sets generous line-height (1.8). Headings inside prose revert to Inter sans-serif.
This is how body text looks inside a prose block. Merriweather at 1.125rem with 1.8 line-height provides comfortable sustained reading. The generous spacing between lines prevents the text from feeling cramped, even in longer articles.
Links inside prose are underlined and blue, distinguishing them from the non-underlined links used elsewhere in the UI. This follows the convention that prose links should be clearly identifiable within running text.
Blockquotes use a 4px left border in primary blue, italic Merriweather, and muted text color. They provide visual distinction for quoted material.
Inline code spans use a light background and smaller font size. Code blocks use the same background with more padding:
// Pre-formatted code blocks
const title = article.title;
const slug = article.slug;
Bold text uses font-weight 700 for emphasis within the serif body copy.
<div class="article-content prose"> <h2>Heading Two</h2> <p>Body text...</p> <blockquote>Quoted text...</blockquote> <ul> <li>List item</li> </ul> <pre><code>Code block</code></pre> </div>
Labels articles by source hub (UX, AI, DESIGN). Two sizes: standard and small.
<span class="article-hub">UX</span> <span class="article-hub article-hub-small">UX</span>
Light gray pills used for article categorization.
<div class="article-tags"> <span class="tag">UX</span> <span class="tag">AI</span> </div>
Used for "Recent Articles", "More Articles" headings. Has a 2px border-bottom.
Centered header with title and subtitle. Used on the articles archive page.
142 articles published
Shown when no articles exist.
Check back soon for new content.
<section class="empty-state"> <h2>No articles yet</h2> <p>Check back soon for new content.</p> </section>
<section class="error-page"> <h1 class="error-title">Oops!</h1> <p class="error-message">Something went wrong</p> <a href="/" class="error-link">← Back to Home</a> </section>
Two sizes: featured (16:10 aspect ratio) and card (16:9 aspect ratio).
.article-card
shadow-lg + translateY(-2px)
a
color → primary-dark (#3a56d4)
.site-logo
color → primary (#4361ee)
.toolbar-select
border-color → primary
.pagination-page
border-color + text → primary
--transition-fast
150ms ease (links, buttons, borders)
--transition-normal
250ms ease (card hover, shadows)
As the article count grows, a search input in the toolbar or header becomes essential. Should follow the existing .toolbar-select styling pattern with matching border radius, padding, and hover/focus states.
<div class="toolbar-control"> <input type="search" class="toolbar-search" placeholder="Search articles..."> </div>
Clickable tags that filter the article list. Reuse the existing .tag class with an active state that swaps to primary blue background and white text.
/* Active tag filter */ .tag-filter { cursor: pointer; transition: all var(--transition-fast); } .tag-filter:hover { background: var(--color-primary); color: white; } .tag-filter.active { background: var(--color-primary); color: white; }
Estimated reading time in the article meta. Would appear alongside the date in .article-meta and .featured-meta with a dot separator.
<div class="article-meta"> <time>February 10, 2026</time> <span class="reading-time">5 min read</span> </div>
An email capture section at the end of articles or in the footer. Should use the primary blue for the submit button, matching the hub badge treatment, and the toolbar-select styling for the email input.
<section class="newsletter-cta"> <h3>Stay in the loop</h3> <p>New articles on UX, AI, and design.</p> <form class="newsletter-form"> <input type="email" class="toolbar-select" placeholder="[email protected]"> <button class="newsletter-submit">Subscribe</button> </form> </section>
The CSS variable architecture supports a dark mode toggle by swapping :root values. All components use variables exclusively; no hard-coded colors in component CSS. A @media (prefers-color-scheme: dark) block or class-based toggle would work without changing any component styles.
/* Dark mode token overrides */ @media (prefers-color-scheme: dark) { :root { --color-bg: #1a1a2e; --color-bg-alt: #252542; --color-text: #f0f0f0; --color-text-muted: #94a3b8; --color-border: #3a3a5c; } }
The home page featured article as it renders in production. 2-column grid with image and content.
When we talk about integrating AI into products, the conversation usually starts with the technology. What model, what parameters, what accuracy. But the real challenge is the design.
Read Article →<section class="featured-section"> <article class="featured-article"> <a href="..." class="featured-image"> <img src="..." alt="..."> </a> <div class="featured-content"> <span class="article-hub">HUB</span> <h1 class="featured-title"> <a href="...">Title</a> </h1> <p class="featured-excerpt">Excerpt...</p> <div class="featured-meta"> <time>Date</time> <div class="article-tags">...</div> </div> <a href="..." class="read-more">Read Article →</a> </div> </article> </section>