content: update building-minimal-design-systems.md, deneme.md, blog-detail.ts (2026-03-11 07:00)

This commit is contained in:
Poyraz Avsever
2026-03-11 10:00:20 +03:00
parent 6e29f0fbf9
commit 2fbf1f532d
3 changed files with 252 additions and 185 deletions
+52 -185
View File
@@ -1,3 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
import matter from "gray-matter";
export type BlogDetail = {
slug: string;
title: string;
@@ -10,202 +14,65 @@ export type BlogDetail = {
markdown: string;
};
export const BLOG_DETAILS: BlogDetail[] = [
{
slug: "building-minimal-design-systems",
title: "Building Minimal Design Systems",
category: "React",
date: "March 2026",
readTime: "12 min read",
author: "Poyraz Avsever",
excerpt:
"A practical and detailed playbook for building a clean, scalable UI system with markdown-driven documentation, mermaid diagrams, and implementation-ready code.",
coverImage: "/news/design.svg",
markdown: `
## Why Most Design Systems Become Heavy
const BLOG_CONTENT_DIR = path.join(process.cwd(), "content", "blog");
Most teams start with good intentions and end up with too many variants, too many exceptions, and no clear usage guide.
The real problem is usually **decision sprawl**:
- Too many size and style combinations
- Inconsistent naming conventions
- Documentation that explains props, but not decisions
---
## Core Principle: Constrain First, Expand Later
Start with a strict base:
1. Typography scale
2. Spacing scale
3. Semantic colors
4. A handful of primitives
> A design system is not a component museum.
> It is an agreement that helps teams ship consistent UI quickly.
---
## Suggested Token Strategy
\`\`\`json
{
"font": {
"size": { "sm": "0.875rem", "base": "1rem", "lg": "1.125rem" },
"weight": { "regular": 400, "medium": 500, "bold": 700 }
},
"space": {
"1": "0.25rem",
"2": "0.5rem",
"3": "0.75rem",
"4": "1rem",
"6": "1.5rem"
}
function toSafeString(value: unknown, fallback: string) {
if (typeof value !== "string") return fallback;
const trimmed = value.trim();
return trimmed || fallback;
}
\`\`\`
---
function normalizeSlug(fileName: string) {
return fileName.replace(/\.md$/i, "");
}
## Architecture Flow
function mapMarkdownToBlogDetail(fileName: string, raw: string): BlogDetail {
const parsed = matter(raw);
const slug = normalizeSlug(fileName);
\`\`\`mermaid
flowchart LR
A[Design Tokens] --> B[Primitives]
B --> C[Composed Components]
C --> D[Page Sections]
D --> E[Product Screens]
\`\`\`
return {
slug,
title: toSafeString(parsed.data.title, slug),
category: toSafeString(parsed.data.category, "General"),
date: toSafeString(parsed.data.date, ""),
readTime: toSafeString(parsed.data.readTime, ""),
author: toSafeString(parsed.data.author, "Poyraz Avsever"),
excerpt: toSafeString(parsed.data.excerpt, ""),
coverImage: toSafeString(parsed.data.coverImage, "/news/design.svg"),
markdown: parsed.content.trim(),
};
}
---
## Example: Button Primitive
\`\`\`tsx
import { cva } from "class-variance-authority";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-sm text-sm font-medium transition-colors",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground",
outline: "border border-border bg-background",
ghost: "bg-transparent"
},
size: {
sm: "h-8 px-3",
md: "h-9 px-4",
lg: "h-10 px-5"
}
},
defaultVariants: {
variant: "default",
size: "md"
}
export async function listBlogDetails(): Promise<BlogDetail[]> {
let files: string[] = [];
try {
files = await fs.readdir(BLOG_CONTENT_DIR);
} catch {
return [];
}
);
\`\`\`
Why this works:
- Variants are explicit
- Defaults are stable
- Consumers avoid ad-hoc class combinations
---
## Documentation Pattern That Scales
Instead of “prop-only docs”, use this pattern for every component:
### 1) When to use
Explain context and intent.
### 2) Do / Don't
Show 2 positive and 2 negative examples.
### 3) Accessibility checklist
- Keyboard interaction
- Focus visibility
- ARIA coverage
### 4) Code examples by complexity
- Basic
- With validation
- With async state
---
## Mermaid Sequence for Contribution Workflow
\`\`\`mermaid
sequenceDiagram
participant Dev as Developer
participant DS as Design System
participant App as Product App
Dev->>DS: Add primitive or variant
DS-->>Dev: Exports + docs update
Dev->>App: Integrate component
App-->>Dev: UX feedback
Dev->>DS: Refine API
\`\`\`
---
## Example: Page-Level Composition
\`\`\`tsx
export function BlogHeader() {
return (
<header className="space-y-2">
<Badge>React</Badge>
<h1 className="text-2xl font-bold">Building Minimal Design Systems</h1>
<p className="text-sm text-muted-foreground">
Practical notes from shipping real components.
</p>
</header>
const markdownFiles = files.filter((fileName) => fileName.endsWith(".md"));
const posts = await Promise.all(
markdownFiles.map(async (fileName) => {
const targetPath = path.join(BLOG_CONTENT_DIR, fileName);
const raw = await fs.readFile(targetPath, "utf8");
return mapMarkdownToBlogDetail(fileName, raw);
}),
);
return posts.sort((a, b) => a.slug.localeCompare(b.slug));
}
\`\`\`
---
export async function getBlogDetailBySlug(slug: string): Promise<BlogDetail | null> {
const safeSlug = slug.trim().toLowerCase();
if (!safeSlug) return null;
## Common Failure Modes
const targetPath = path.join(BLOG_CONTENT_DIR, `${safeSlug}.md`);
| Problem | Why it happens | Better approach |
|---|---|---|
| Variant explosion | No ownership rules | Add contribution guardrails |
| Styling overrides everywhere | Weak defaults | Strengthen semantic tokens |
| Inconsistent docs | No template | Use one markdown template |
| Slow adoption | API too abstract | Show direct usage examples |
---
## Final Checklist Before Shipping a New Component
- [ ] API is minimal and explicit
- [ ] States are documented (default, hover, focus, disabled, loading)
- [ ] Keyboard support verified
- [ ] Mobile rendering verified
- [ ] Real usage example added to docs
---
## Closing Notes
A minimal design system should feel boring in the best way.
When engineers can predict behavior, teams move faster with fewer regressions.
Focus on **consistency, clarity, and constraints**.
Those three will outperform “feature-rich” component sets in the long run.
`,
},
] as const;
export function getBlogDetailBySlug(slug: string) {
return BLOG_DETAILS.find((post) => post.slug === slug) ?? null;
try {
const raw = await fs.readFile(targetPath, "utf8");
return mapMarkdownToBlogDetail(`${safeSlug}.md`, raw);
} catch {
return null;
}
}