Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ src/
├── index.css # Design tokens (CSS custom properties), font imports, base resets
├── components/
│ ├── atoms/ # Smallest building blocks (10 components)
│ ├── molecules/ # Compositions of atoms (18 components)
│ ├── atoms/ # Smallest building blocks (11 components)
│ ├── molecules/ # Compositions of atoms (17 components)
│ ├── organisms/ # Major UI sections (19 components)
│ ├── templates/ # Page layout shells (7 components)
│ └── ui/ # shadcn/ui primitives (40+ components)
│ └── ui/ # shadcn/ui primitives (48 components)
├── pages/ # Route pages (showcase + prototype)
├── hooks/ # Custom React hooks
Expand All @@ -113,11 +113,11 @@ src/

| Layer | Count | Description |
| --- | --- | --- |
| **Atoms** | 10 | Typography, code, tags, links, copy button, spinner |
| **Molecules** | 18 | Form fields, nav items, stat cards, search, diff, variable editors |
| **Atoms** | 11 | Typography, code, tags, links, copy button, spinner |
| **Molecules** | 17 | Form fields, nav items, stat cards, search, diff, variable editors |
| **Organisms** | 19 | Navigation, tables, auth, settings, data management, API tooling |
| **Templates** | 7 | Page layout shells (app shell, editor, dashboard, comparison) |
| **shadcn/ui primitives** | 40+ | Radix UI + shadcn base components |
| **shadcn/ui primitives** | 48 | Radix UI + shadcn base components |

> **Examples:** [`src/examples/prompt-x/`](src/examples/prompt-x/) contains 23 organisms + 2 molecules built for prompt-engineering UIs. They are not part of the public design system surface, but stand as fully-functional reference implementations of how to compose democrito atoms into a real product.

Expand Down
41 changes: 41 additions & 0 deletions src/__tests__/tokenizeBrackets.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";

import { tokenizeBrackets } from "@/lib/tokenizeBrackets";

describe("tokenizeBrackets", () => {
it("wraps a single bracketed token in a highlighted span", () => {
render(<>{tokenizeBrackets("Generate [product]")}</>);
const span = screen.getByText("[product]");
expect(span.tagName).toBe("SPAN");
expect(span.className).toContain("text-accent");
});

it("renders exactly one highlighted span for a single token", () => {
const { container } = render(<>{tokenizeBrackets("Generate [product]")}</>);
expect(container.querySelectorAll("span")).toHaveLength(1);
});

it("preserves the original string for copy — no bracket stripping", () => {
const input = "Generate [product]";
// The raw input must be untouched (copy value is always `input`, not the nodes)
expect(input).toBe("Generate [product]");
});

it("highlights multiple bracketed tokens", () => {
const { container } = render(<>{tokenizeBrackets("Use [product] and [brand direction]")}</>);
expect(container.querySelectorAll("span")).toHaveLength(2);
expect(screen.getByText("[product]")).toBeTruthy();
expect(screen.getByText("[brand direction]")).toBeTruthy();
});

it("does not highlight text without brackets", () => {
const { container } = render(<>{tokenizeBrackets("plain text only")}</>);
expect(container.querySelectorAll("span")).toHaveLength(0);
});

it("does not highlight tokens that span a newline", () => {
const { container } = render(<>{tokenizeBrackets("bad [\nmultiline]")}</>);
expect(container.querySelectorAll("span")).toHaveLength(0);
});
});
3 changes: 2 additions & 1 deletion src/components/atoms/CodeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { forwardRef } from "react";

import { CopyButton } from "@/components/atoms/CopyButton";
import { cn } from "@/lib/utils";
import { tokenizeBrackets } from "@/lib/tokenizeBrackets";

interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
/** The code to render and optionally copy. Preserve whitespace exactly as written. */
Expand Down Expand Up @@ -46,7 +47,7 @@ const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
role="code"
className="overflow-x-auto whitespace-pre font-mono text-sm text-foreground"
>
{code}
{tokenizeBrackets(code)}
</pre>
</div>
);
Expand Down
17 changes: 14 additions & 3 deletions src/components/organisms/ai/FileArchitectureSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,31 @@ const FILES: FileCardData[] = [
rawUrl: "https://raw.githubusercontent.com/mmorerasanchez/democrito/main/DESIGN.md",
},
{
filename: "DESIGN_SYSTEM.md",
filename: "docs/design-system.md",
role: "Token Inventory",
description:
"Complete reference of CSS custom properties, component inventory, variant specifications. The machine-readable specification.",
githubUrl: "https://github.com/mmorerasanchez/democrito/blob/main/docs/design-system.md",
rawUrl: "https://raw.githubusercontent.com/mmorerasanchez/democrito/main/docs/design-system.md",
},
{
filename: "AGENTS.md",
role: "Cross-tool Agent Context",
description:
"The tool-agnostic sibling of CLAUDE.md — same project rules and architecture, in the format Cursor, Copilot, and Codex auto-load. One repo, every agent on-system.",
githubUrl: "https://github.com/mmorerasanchez/democrito/blob/main/AGENTS.md",
rawUrl: "https://raw.githubusercontent.com/mmorerasanchez/democrito/main/AGENTS.md",
},
];

export function FileArchitectureSection() {
return (
<section className="space-y-4">
<Heading level="h2">Three-file architecture</Heading>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<Heading level="h2">Agent context architecture</Heading>
<Text variant="muted" size="sm" className="max-w-2xl">
Four plain-text files give any AI agent the rules, the reasoning, and the inventory it needs to build on-system — loaded automatically by the tools that read them.
</Text>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{FILES.map((file) => (
<Card
key={file.filename}
Expand Down
24 changes: 24 additions & 0 deletions src/lib/tokenizeBrackets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";

const SPLIT_RE = /(\[[^\]\n]+\])/g;
const MATCH_RE = /^\[[^\]\n]+\]$/;

const HIGHLIGHT_CLASS = "rounded-sm bg-accent/10 px-1 text-accent";

/**
* Splits `text` on [bracketed] tokens and wraps each match in a highlighted span.
* Plain segments are returned as strings. The raw `text` is unchanged and should
* be used as the copy-to-clipboard value.
*/
export function tokenizeBrackets(text: string): React.ReactNode[] {
const parts = text.split(SPLIT_RE);
return parts.map((part, i) =>
MATCH_RE.test(part) ? (
<span key={i} className={HIGHLIGHT_CLASS}>
{part}
</span>
) : (
part
),
);
}
33 changes: 13 additions & 20 deletions src/pages/AiPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { PageMeta } from "@/components/PageMeta";
import { Heading } from "@/components/atoms";
import { Badge } from "@/components/ui/badge";
import { Link } from "react-router-dom";
import { ArrowRight, Github, Heart } from "lucide-react";
import { ArrowRight, Github, Heart, Layers } from "lucide-react";
import { FileArchitectureSection } from "@/components/organisms/ai";

function ClaudeIcon({ className }: { className?: string }) {
Expand All @@ -24,22 +24,21 @@ const platforms = [
path: "/ai/claude",
icon: ClaudeIcon,
name: "Claude",
badge: "Anthropic",
desc: "Attach design-tokens.json as context and describe your brand. Claude generates a complete new theme in natural language.",
},
{
path: "/ai/vibe-coding",
icon: Heart,
name: "Vibe Coding Tools",
badge: "Lovable · Google Stitch · Replit",
desc: "Fork the repo and prompt your visual direction. No terminal needed.",
name: "Vibe Coding",
},
{
path: "/ai/github",
icon: Github,
name: "Terminal",
badge: "GitHub",
desc: "Edit src/index.css directly. Two variables change the whole personality.",
},
{
path: "/ai/examples",
icon: Layers,
name: "Examples",
},
];

Expand Down Expand Up @@ -75,14 +74,14 @@ export default function AiPage() {

<FileArchitectureSection />

{/* Distribution */}
{/* Customization */}
<section className="space-y-4">
<Heading level="h2">Distribution</Heading>
<Heading level="h2">Customization</Heading>
<p className="font-body text-base text-muted-foreground max-w-2xl">
Claude, vibe coding platforms, and terminal workflows — each
path fits a different setup.
path fits a different setup, with examples.
</p>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{platforms.map((item) => (
<Link
key={item.path}
Expand All @@ -93,14 +92,8 @@ export default function AiPage() {
<item.icon className="h-4 w-4 text-accent" />
<ArrowRight className="h-3.5 w-3.5 text-muted-foreground group-hover:text-accent transition-colors" />
</div>
<div>
<p className="font-display text-sm font-semibold text-foreground group-hover:text-accent transition-colors">
{item.name}
</p>
<p className="font-mono text-xs text-muted-foreground">{item.badge}</p>
</div>
<p className="font-body text-sm text-muted-foreground">
{item.desc}
<p className="font-display text-sm font-semibold text-foreground group-hover:text-accent transition-colors">
{item.name}
</p>
</Link>
))}
Expand Down
10 changes: 5 additions & 5 deletions src/pages/OverviewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ function ClaudeIcon({ className }: { className?: string }) {

const stats = [
{ label: "Design tokens", count: "90+" },
{ label: "Atoms", count: "10" },
{ label: "Molecules", count: "18" },
{ label: "Atoms", count: "11" },
{ label: "Molecules", count: "17" },
{ label: "Organisms", count: "19" },
{ label: "Templates", count: "7" },
{ label: "UI primitives", count: "40+" },
{ label: "Templates", count: "7" },
{ label: "UI primitives", count: "48" },
];

const principles = [
Expand Down Expand Up @@ -102,7 +102,7 @@ export default function OverviewPage() {
<Badge variant="outline">v3</Badge>
</div>
<p className="font-body text-base text-muted-foreground max-w-prose">
Agnostic, hand-crafted atomic design system for AI-native apps: 3 themes, structured tokens, accessible components; adaptable via Claude, vibe coding platforms or terminal.
Agnostic atomic design system for AI-native tools — structured tokens, accessible components, and three-theme support. React + Tailwind CSS + Radix UI. Adaptable via Claude, vibe coding platforms or terminal.
</p>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
<Button onClick={() => window.open(REPO_URL, "_blank")}>
Expand Down
5 changes: 4 additions & 1 deletion src/pages/ai/_shared.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CopyButton } from "@/components/atoms/CopyButton";
import { tokenizeBrackets } from "@/lib/tokenizeBrackets";

export function StepCode({ language, children }: { language: string; children: string }) {
return (
Expand All @@ -13,7 +14,9 @@ export function StepCode({ language, children }: { language: string; children: s
/>
</div>
<pre className="overflow-x-auto bg-muted p-4">
<code className="font-mono text-xs text-foreground whitespace-pre">{children}</code>
<code className="font-mono text-xs text-foreground whitespace-pre">
{tokenizeBrackets(children)}
</code>
</pre>
</div>
);
Expand Down
Loading