diff --git a/.gitignore b/.gitignore index e4e0199..ec534b6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,15 @@ .claude/* !.claude/rules/ !.claude/rules/*.md + +# Auth / secrets — never commit (see docs/design-extract-auth.md) +.auth/ +**/storage-state.json +**/*cookie*.txt +scripts/node_modules/ +scripts/package-lock.json + +# Local env overrides +.env +.env.* +!.env.example diff --git a/docs/design-extract-auth.md b/docs/design-extract-auth.md new file mode 100644 index 0000000..203477a --- /dev/null +++ b/docs/design-extract-auth.md @@ -0,0 +1,137 @@ +# Design extract — authenticated access to coulomb.social + +designlang can crawl authenticated pages via Playwright cookies / storage state: + +```text +npx designlang … --cookie-file +``` + +Supported cookie file formats: JSON, Playwright `storageState`, Netscape `cookies.txt`. + +## Threat model + +| Artefact | Sensitivity | Where it lives | +|----------|-------------|----------------| +| Bubble member password | **High** — full account | OpenBao only (never Git/chat) | +| Browser `storageState` | **High** — session impersonation | Local XDG path, mode `0600`, gitignored | +| designlang output (tokens, screenshots) | Product UI — ok in repo after review | `docs/design-extract/` | + +Do **not** paste passwords, cookies, or storage-state JSON into chat, workplans, or commits. + +## Preferred flow (interactive session) + +Bubble login may involve MFA, email codes, or CAPTCHA. Prefer a **human-attended** headed browser once, then reuse session state for extracts. + +### 1. Capture session as your user + +```bash +cd ~/coulomb-social +./scripts/capture-auth-state.sh +``` + +1. A Chromium window opens on `https://coulomb.social`. +2. Log in as **your** member account and navigate until you see the authenticated UI you care about. +3. Return to the terminal and press **Enter**. +4. Session is written to: + +```text +~/.config/coulomb-social/auth/storage-state.json # mode 0600 +``` + +### 2. Run the extract + +```bash +./scripts/run-design-extract.sh +# or with extra flags: +./scripts/run-design-extract.sh --depth 5 --full +``` + +Default output: `docs/design-extract/` (safe to commit after review; no secrets). + +### 3. Refresh when the session expires + +Re-run `./scripts/capture-auth-state.sh`. There is no long-lived cookie vault unless you complete the OpenBao lane below. + +## OpenBao custody (password for re-login / automation) + +Use when you need **unattended** re-auth (CI, agent loops) without a headed browser. + +| Field | Value | +|-------|--------| +| Mount | `tenants` | +| Path | `tenants/binky/coulomb-social/bubble-member` | +| Fields | `EMAIL`, `PASSWORD` | +| Risk | `high` (member account) | +| Tenant | `binky` (Binky Hedgehog = client #1) | +| Workload | `coulomb-social` | +| Bundle | `bubble-member` | + +This follows `ops-warden/wiki/playbooks/tenant-secret-onboarding.md`. **Status today:** path and fields are **declared** for this repo; the CCR, policy, OIDC role, and catalog entry are **not yet applied**. Until then, use interactive capture only. + +### Founder provision (after CCR is applied) + +```bash +# values only in mode-0600 files — never argv or chat +printf '%s' 'you@example.com' > /tmp/cs-email +printf '%s' '…' > /tmp/cs-pass +chmod 600 /tmp/cs-email /tmp/cs-pass + +bao login -method=oidc -path=netkingdom # admin / net-kingdom-admins as required +bao kv put tenants/binky/coulomb-social/bubble-member \ + EMAIL=@/tmp/cs-email \ + PASSWORD=@/tmp/cs-pass + +shred -u /tmp/cs-email /tmp/cs-pass +``` + +### Worker fetch (never print) + +```bash +bao login -method=oidc -path=netkingdom role=-workload-kv-read +bao kv get -field=EMAIL tenants/binky/coulomb-social/bubble-member > ~/.config/coulomb-social/auth/email +bao kv get -field=PASSWORD tenants/binky/coulomb-social/bubble-member > ~/.config/coulomb-social/auth/password +chmod 600 ~/.config/coulomb-social/auth/email ~/.config/coulomb-social/auth/password +``` + +Automated login-from-password is optional (`./scripts/capture-auth-state.sh --from-password-files`) and only works if bubble accepts simple email/password without interactive MFA. + +## Routing (agents) + +```bash +warden route find "coulomb.social bubble member login" +``` + +Until a catalog id exists, treat this as: + +| Need | Owner | +|------|--------| +| Member password | OpenBao `tenants/binky/…` (founder provision) | +| Interactive session | You — `scripts/capture-auth-state.sh` | +| SSH / tunnels | ops-warden / ops-bridge (unrelated) | + +**Anti-patterns:** message ops-warden for the password; commit `storage-state.json`; put `EMAIL`/`PASSWORD` in `.env` in the repo. + +## designlang auth flags (reference) + +**Important:** use the **main** `designlang ` command, not `designlang pack`. +The `pack` subcommand ignores `--cookie-file` and crawls anonymously. + +```bash +# Prefer the wrapper (probes session, applies cookies, correct out dir): +./scripts/run-design-extract.sh + +# Or manually: +npx designlang https://coulomb.social \ + --cookie-file ~/.config/coulomb-social/auth/storage-state.json \ + -o docs/design-extract \ + -n coulomb-social \ + --wait 5000 \ + --depth 2 \ + --interactions +``` + +Bubble reuses the same `coulomb_*` cookie **names** for guests and members. +A real member session is verified by landing on an app route (e.g. `/vw_pages`) +after load, not by cookie count. + +Chrome extension path (optional): log in in desktop Chrome, use designlang’s extension for a one-click handoff if you prefer not to use Playwright capture. diff --git a/docs/design-extract/PROVENANCE.md b/docs/design-extract/PROVENANCE.md new file mode 100644 index 0000000..32b33d8 --- /dev/null +++ b/docs/design-extract/PROVENANCE.md @@ -0,0 +1,26 @@ +# Design extract provenance + +| Field | Value | +|-------|--------| +| Source | https://coulomb.social | +| Tool | designlang main extract (not `pack`) | +| Captured | 2026-08-09 | +| Auth | Playwright storageState via `--cookie-file` | +| Session probe | https://coulomb.social/vw_pages (Research \| Coulomb) | +| Pages | 7 analyzed · 3445 elements | +| Primary | `#d08728` | +| designlang exit | 1 (late summary crash after writing core files) | + +## Important + +`designlang pack` **does not apply cookies**. Always use +`./scripts/run-design-extract.sh` (main extract command). + +## Key artifacts + +- `coulomb-social-design-language.md` — LLM-oriented full description +- `coulomb-social-DESIGN.md` — agent-native DESIGN.md +- `coulomb-social-design-tokens.json` — DTCG tokens +- `coulomb-social-variables.css` / `coulomb-social-tailwind.config.js` +- `coulomb-social-preview.html` — visual preview +- `coulomb-social-voice.json` — UI chrome (Material icon buttons on Research view) diff --git a/docs/design-extract/coulomb-social-AGENT.md b/docs/design-extract/coulomb-social-AGENT.md new file mode 100644 index 0000000..83e27cb --- /dev/null +++ b/docs/design-extract/coulomb-social-AGENT.md @@ -0,0 +1,115 @@ +# You are building UI in the coulomb.social design system. + +Source: https://coulomb.social +Extracted by designlang on 2026-08-08. + +## Brand at a glance + +- title Research | Coulomb +- page intent landing +- material flat +- design grade B + +## Colour + +- primary #d08728 +- neutrals #000000 · #efefef · #616161 · #373737 · #171717 · #ffffff + +## Typography + +- families Helvetica · Arial · Times New Roman · Inter +- weights 400 · 500 · 600 · 700 +- base size 16px + +## Spacing + +- scale 1px · 24px · 32px · 40px · 60px · 80px · 96px · 320px + +## Radii + +- scale 4px · 8px · 12px · 15px · 20px + +## Motion + +- durations 250ms + +## Voice + +- tone neutral +- pronoun third-person +- headings unknown +- CTA verbs content · favorite · format · filter · add · arrow + +## Component anatomy + +- button variants: [object Object] · slots: true · true · false + +## Accessibility + +- WCAG score 100% · failing pairs: 0 + +## Build rules + +1. Use the colours above. **Never invent a new hex.** If you need a + shade between two existing colours, derive it via HSL adjustment + from the closest extracted colour and call out the derivation. +2. Use the extracted typography families. If you need a missing weight, + pick the nearest available weight from the list and note it. +3. Snap spacing values to the scale above. No off-scale paddings or + margins. +4. Snap border radii to the scale above. +5. Match the voice: same tone, same pronoun stance, same heading + style. Reuse the listed CTA verbs. +6. Aim for WCAG AA contrast minimum. When the brand colours fail, + prefer the foreground colour on the background colour rather than + mid-tone neutrals. +7. Reuse component anatomy when it exists — do not invent novel + structures for things the site already has. + +## Available context files + +designlang wrote these alongside this prompt. Reach for them when +you need ground truth: + +- `-design-tokens.json` — DTCG primitive · semantic · composite tokens +- `-tailwind.config.js` — Tailwind v3 config +- `-tailwind-v4.css` — Tailwind v4 `@theme` block +- `-tokens.d.ts` — TypeScript literal-union types +- `-variables.css` — bare CSS custom properties +- `-reset.css` — brand-aware base styles +- `-gradients.css` — `.grad-N` utility classes +- `-anatomy.tsx` — typed React component scaffolds +- `-shadcn-theme.css` — shadcn/ui theme +- `-theme.js` — React / Vue / Svelte theme object +- `-mcp.json` — MCP server payload (load via stdio) +- `.brand.pdf` — print-ready 13-chapter brand book + +When you reference the system in code, prefer importing from these +files over hard-coding values. + +## Output expectations + +When asked to "build a pricing page" or "make a card" or any UI: + +- Produce a single self-contained component file in the appropriate + framework (React / Vue / Svelte — match what the user is using). +- Use Tailwind utility classes wired to the v4 `@theme` if Tailwind + is available; otherwise use the CSS custom properties from + `variables.css`. +- Write the headline copy using the brand voice; do not invent + generic Lorem. +- Annotate any choice where you had to bend the system, with a + one-line `// note:` comment explaining what and why. + +## One-line install + +```bash +npx designlang coulomb.social +``` + +Run this against any other URL to extract its system in the same +shape as the one above. + +--- + +Generated by designlang. Re-extract by running `npx designlang coulomb.social`. diff --git a/docs/design-extract/coulomb-social-DESIGN.md b/docs/design-extract/coulomb-social-DESIGN.md new file mode 100644 index 0000000..d87a22d --- /dev/null +++ b/docs/design-extract/coulomb-social-DESIGN.md @@ -0,0 +1,109 @@ +--- +site: "Research | Coulomb" +url: "https://coulomb.social" +generated_at: "2026-08-08T23:30:22.030Z" +generator: "designlang@12.21.0" +intent: landing +material: flat +tokens: + colors: + primary: "#d08728" + background: "#ffffff" + foreground: "#000000" + typography: + sans: Helvetica + spacing: + base: 4 + scale: "[1, 24, 32, 40, 60, 80, 96, 320]" + radii: + sm: 4 + md: 8 + lg: 15 + xl: 20 + shadows: + sm: "rgb(170, 170, 170) 2px 2px 4px 0px" + md: "rgba(23, 23, 23, 0.1) 2px 2px 8px 2px" +--- + +# Overview + +A **landing** page (heuristic confidence 0.45), dressed in **flat** material. + +> "Research | Coulomb" + +The author writes in a **neutral** voice; headings tend to be **unknown** case and **tight**. + +Reading order detected on the source: `—`. + +Source: . + +# Colors + +| role | hex | usage | +|---|---|---| +| primary | `#d08728` | 8 | +| background | `#ffffff` | — | +| foreground | `#000000` | — | + +**Neutrals:** `#000000` · `#efefef` · `#616161` · `#373737` · `#171717` + +**Total unique colors detected:** 8. + +# Typography + +**Families** +- `Helvetica` · 1739 uses +- `Arial` · 761 uses +- `Times New Roman` · 450 uses +- `Inter` · 397 uses + +# Layout + +**Spacing base:** `4px` increments. +**Scale:** `1px` · `24px` · `32px` · `40px` · `60px` · `80px` · `96px` · `320px` + +**Layout primitives:** 123 grid containers · 1256 flex containers. + +# Elevation and Depth + +**Shadow scale** +- `sm` — `rgb(170, 170, 170) 2px 2px 4px 0px` +- `md` — `rgba(23, 23, 23, 0.1) 2px 2px 8px 2px` + +**Z-index layers:** 35 + +# Shapes + +**Radius scale** +- `sm` — `4px` +- `md` — `8px` +- `lg` — `12px` +- `lg` — `15px` +- `xl` — `20px` + +# Components + +**Detected patterns:** `buttons` · `cards` · `inputs` · `links` · `modals` + +**Anatomy** +| kind | variants | sizes | instances | +|---|---|---|---| +| button | — | — | 56 | + +# Do's and Don'ts + +**Do's** +- Use `content`, `favorite`, `format` as the primary verbs in CTAs — these dominate the source. +- Write headings in **unknown** case, **tight** length. +- Address the reader with the pronoun posture **third-person**. +- Stay inside the **flat** material — match shadow and radius habits. + +**Don'ts** +- Don't 4 font families — consider limiting to 2 (heading + body). +- Don't 140 !important rules — prefer specificity over overrides. +- Don't 81% of css is unused — consider purging. +- Don't 6545 duplicate css declarations. + +--- +_Generated by [designlang](https://github.com/Manavarya09/design-extract) v12.21.0 from ._ +_Compatible with the DESIGN.md convention pioneered by [design-extractor.com](https://www.design-extractor.com) — extended with intent, material, voice, anatomy, and library detection._ diff --git a/docs/design-extract/coulomb-social-anatomy.tsx b/docs/design-extract/coulomb-social-anatomy.tsx new file mode 100644 index 0000000..05279fe --- /dev/null +++ b/docs/design-extract/coulomb-social-anatomy.tsx @@ -0,0 +1,15 @@ +// Auto-generated by designlang — component anatomy v2. +// Scaffolds. Wire into your token system; not a runtime library. + +import * as React from 'react'; + +export interface ButtonProps { + variant?: 'default'; + size?: 'md'; + leadingIcon?: React.ReactNode; + children?: React.ReactNode; +} + +export function Button({ variant = 'default', size = 'md', ...rest }: ButtonProps) { + return React.createElement('button', { 'data-variant': variant, 'data-size': size, ...rest }); +} diff --git a/docs/design-extract/coulomb-social-design-language.md b/docs/design-extract/coulomb-social-design-language.md new file mode 100644 index 0000000..9ff1248 --- /dev/null +++ b/docs/design-extract/coulomb-social-design-language.md @@ -0,0 +1,578 @@ +# Design Language: Research | Coulomb + +> Extracted from `https://coulomb.social` on August 9, 2026 +> 3445 elements analyzed across 7 pages + +This document describes the complete design language of the website. It is structured for AI/LLM consumption — use it to faithfully recreate the visual design in any framework. + +## Color Palette + +### Primary Colors + +| Role | Hex | RGB | HSL | Usage Count | +|------|-----|-----|-----|-------------| +| Primary | `#d08728` | rgb(208, 135, 40) | hsl(34, 68%, 49%) | 8 | + +### Neutral Colors + +| Hex | HSL | Usage Count | +|-----|-----|-------------| +| `#000000` | hsl(0, 0%, 0%) | 5304 | +| `#efefef` | hsl(0, 0%, 94%) | 610 | +| `#616161` | hsl(0, 0%, 38%) | 556 | +| `#373737` | hsl(0, 0%, 22%) | 462 | +| `#171717` | hsl(0, 0%, 9%) | 401 | +| `#ffffff` | hsl(0, 0%, 100%) | 242 | +| `#e5e5e5` | hsl(0, 0%, 90%) | 217 | + +### Background Colors + +Used on large-area elements: `#ffffff`, `#e5e5e5`, `#171717`, `#f5f5f5` + +### Text Colors + +Text color palette: `#000000`, `#171717`, `#373737`, `#616161`, `#ffffff` + +### Full Color Inventory + +| Hex | Contexts | Count | +|-----|----------|-------| +| `#000000` | text, border, background | 5304 | +| `#efefef` | background | 610 | +| `#616161` | text, border | 556 | +| `#373737` | text, border | 462 | +| `#171717` | text, border, background | 401 | +| `#ffffff` | background, border, text | 242 | +| `#e5e5e5` | border, background | 217 | +| `#d08728` | background | 8 | + +## Typography + +### Font Families + +- **Helvetica** — used for body (1739 elements) +- **Arial** — used for body (761 elements) +- **Times New Roman** — used for body (450 elements) +- **Inter** — used for body (397 elements) + +### Type Scale + +| Size (px) | Size (rem) | Weight | Line Height | Letter Spacing | Used On | +|-----------|------------|--------|-------------|----------------|---------| +| 50px | 3.125rem | 400 | 50px | normal | text | +| 42px | 2.625rem | 700 | normal | normal | input | +| 24px | 1.5rem | 600 | 36px | normal | a | +| 18px | 1.125rem | 600 | 18px | normal | div | +| 16px | 1rem | 400 | normal | normal | html, head, meta, title | +| 14px | 0.875rem | 500 | 19.6px | normal | div, a | +| 13.3333px | 0.8333rem | 400 | normal | normal | button, svg, use | + +### Font Weights in Use + +`400` (3048x), `500` (287x), `600` (102x), `700` (8x) + +## Spacing + +**Base unit:** 4px + +| Token | Value | Rem | +|-------|-------|-----| +| spacing-1 | 1px | 0.0625rem | +| spacing-24 | 24px | 1.5rem | +| spacing-32 | 32px | 2rem | +| spacing-40 | 40px | 2.5rem | +| spacing-60 | 60px | 3.75rem | +| spacing-80 | 80px | 5rem | +| spacing-96 | 96px | 6rem | +| spacing-320 | 320px | 20rem | + +## Border Radii + +| Label | Value | Count | +|-------|-------|-------| +| sm | 4px | 119 | +| md | 8px | 122 | +| lg | 12px | 110 | +| lg | 15px | 6 | +| xl | 20px | 12 | + +## Box Shadows + +**sm** — blur: 4px +```css +box-shadow: rgb(170, 170, 170) 2px 2px 4px 0px; +``` + +**md** — blur: 8px +```css +box-shadow: rgba(23, 23, 23, 0.1) 2px 2px 8px 2px; +``` + +## CSS Custom Properties + +### Colors + +```css +--color_primary_default: rgba(2, 5, 211, 1); +--color_primary_default_rgb: 2, 5, 211; +--color_primary_contrast_default: rgba(255, 255, 255, 1); +--color_primary_contrast_default_rgb: 255, 255, 255; +--color_text_default: rgba(9, 23, 71, 1); +--color_text_default_rgb: 9, 23, 71; +--color_surface_default: rgba(255, 255, 255, 1); +--color_surface_default_rgb: 255, 255, 255; +--color_background_default: rgba(255, 255, 255, 0); +--color_background_default_rgb: 255, 255, 255; +--color_destructive_default: rgba(255, 0, 0, 1); +--color_destructive_default_rgb: 255, 0, 0; +--color_success_default: rgba(23, 219, 78, 1); +--color_success_default_rgb: 23, 219, 78; +--color_alert_default: rgba(250, 181, 21, 1); +--color_alert_default_rgb: 250, 181, 21; +--color_danger_default_rgb: rgba(176, 32, 12, 1); +--color_bTRUr_default_rgb: 208,135,40; +--color_bTOCs_default: rgba(55,55,55,1); +--color_bTHZK_default_rgb: 229,229,229; +--color_bTOCs_default_rgb: 55,55,55; +--color_bTINZ_default_rgb: 23,23,23; +--color_bTRUs_default_rgb: 227,230,235; +--color_bTINZ_default: rgba(23,23,23,0.36); +--color_bTRUr_default: rgba(208,135,40,0.1); +--color_bTHZK_default: rgba(229,229,229,1); +--color_bTHfJ_default_rgb: 97,97,97; +--color_bTMap_default: rgba(113,2,139,1); +--color_bTHfJ_default: rgba(97,97,97,1); +--color_bTMap_default_rgb: 113,2,139; +--color_bTRUs_default: rgba(227,230,235,1); +``` + +### Typography + +```css +--font_default: 'Lato'; +--font_bTRUx_default: "Inter"; +--font_bTRUt_default: "Inter"; +``` + +### Semantic + +```css +success: [object Object]; +warning: [object Object]; +error: [object Object]; +info: [object Object]; +``` + +## Transitions & Animations + +**Durations:** `0.25s` + +### Common Transitions + +```css +transition: all; +transition: background 0.25s; +``` + +### Keyframe Animations + +**highlight-in** +```css +@keyframes highlight-in { + 0% { background-color: rgb(200, 0, 0); } +} +``` + +**fa-spin** +```css +@keyframes fa-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(359deg); } +} +``` + +**opacity-70-25-0-13** +```css +@keyframes opacity-70-25-0-13 { + 0% { opacity: 0.25; } + 0.01% { opacity: 0.25; } + 0.02% { opacity: 1; } + 70.01% { opacity: 0.25; } + 100% { opacity: 0.25; } +} +``` + +**opacity-70-25-1-13** +```css +@keyframes opacity-70-25-1-13 { + 0% { opacity: 0.25; } + 7.70231% { opacity: 0.25; } + 7.71231% { opacity: 1; } + 77.7023% { opacity: 0.25; } + 100% { opacity: 0.25; } +} +``` + +**opacity-70-25-2-13** +```css +@keyframes opacity-70-25-2-13 { + 0% { opacity: 0.25; } + 15.3946% { opacity: 0.25; } + 15.4046% { opacity: 1; } + 85.3946% { opacity: 0.25; } + 100% { opacity: 0.25; } +} +``` + +**opacity-70-25-3-13** +```css +@keyframes opacity-70-25-3-13 { + 0% { opacity: 0.25; } + 23.0869% { opacity: 0.25; } + 23.0969% { opacity: 1; } + 93.0869% { opacity: 0.25; } + 100% { opacity: 0.25; } +} +``` + +**opacity-70-25-4-13** +```css +@keyframes opacity-70-25-4-13 { + 0% { opacity: 0.258349; } + 30.7792% { opacity: 0.25; } + 30.7892% { opacity: 1; } + 0.779231% { opacity: 0.25; } + 100% { opacity: 0.258349; } +} +``` + +**opacity-70-25-5-13** +```css +@keyframes opacity-70-25-5-13 { + 0% { opacity: 0.340766; } + 38.4715% { opacity: 0.25; } + 38.4815% { opacity: 1; } + 8.47154% { opacity: 0.25; } + 100% { opacity: 0.340766; } +} +``` + +**opacity-70-25-6-13** +```css +@keyframes opacity-70-25-6-13 { + 0% { opacity: 0.423184; } + 46.1638% { opacity: 0.25; } + 46.1738% { opacity: 1; } + 16.1638% { opacity: 0.25; } + 100% { opacity: 0.423184; } +} +``` + +**opacity-70-25-7-13** +```css +@keyframes opacity-70-25-7-13 { + 0% { opacity: 0.505602; } + 53.8562% { opacity: 0.25; } + 53.8662% { opacity: 1; } + 23.8562% { opacity: 0.25; } + 100% { opacity: 0.505602; } +} +``` + +## Component Patterns + +Detected UI component patterns and their most common styles: + +### Buttons (643 instances) + +```css +.button { + background-color: rgb(239, 239, 239); + color: rgb(0, 0, 0); + font-size: 13.3333px; + font-weight: 400; + padding-top: 1px; + padding-right: 6px; + border-radius: 0px; +} +``` + +### Cards (36 instances) + +```css +.card { + background-color: rgb(255, 255, 255); + border-radius: 4px; + box-shadow: rgb(170, 170, 170) 2px 2px 4px 0px; + padding-top: 4px; + padding-right: 4px; +} +``` + +### Inputs (16 instances) + +```css +.input { + color: rgb(23, 23, 23); + border-color: rgb(255, 255, 255); + border-radius: 8px; + font-size: 16px; + padding-top: 2px; + padding-right: 2px; +} +``` + +### Links (130 instances) + +```css +.link { + color: rgb(23, 23, 23); + font-size: 16px; + font-weight: 500; +} +``` + +### Modals (78 instances) + +```css +.modal { + background-color: rgb(255, 255, 255); + border-radius: 12px; + padding-top: 24px; + padding-right: 0px; + max-width: 280px; +} +``` + +## Component Clusters + +Reusable component instances grouped by DOM structure and style similarity: + +### Button — 55 instances, 1 variant + +**Variant 1** (55 instances) + +```css + background: rgba(0, 0, 0, 0); + color: rgb(55, 55, 55); + padding: 0px 0px 0px 0px; + border-radius: 4px; + border: 0px none rgb(55, 55, 55); + font-size: 13.3333px; + font-weight: 400; +``` + +### Button — 1 instance, 1 variant + +**Variant 1** (1 instance) + +```css + background: rgb(239, 239, 239); + color: rgb(0, 0, 0); + padding: 1px 6px 1px 6px; + border-radius: 0px; + border: 2px outset rgb(0, 0, 0); + font-size: 13.3333px; + font-weight: 400; +``` + +## Layout System + +**123 grid containers** and **1256 flex containers** detected. + +### Container Widths + +| Max Width | Padding | +|-----------|---------| +| 1200px | 0px | +| 100% | 0px | +| 992px | 0px | + +### Grid Column Patterns + +| Columns | Usage Count | +|---------|-------------| +| 1-column | 122x | +| 2-column | 1x | + +### Grid Templates + +```css +grid-template-columns: 592px 592px; +gap: 16px; +grid-template-columns: 32px; +grid-template-columns: 32px; +grid-template-columns: 32px; +grid-template-columns: 32px; +``` + +### Flex Patterns + +| Direction/Wrap | Count | +|----------------|-------| +| column/nowrap | 470x | +| row/nowrap | 71x | +| row/wrap | 715x | + +**Gap values:** `0px 12px`, `0px 20px`, `0px 24px`, `0px 4px`, `0px 8px`, `0px normal`, `12px normal`, `16px`, `18px normal`, `24px`, `24px normal`, `4px normal`, `8px normal` + +## Interaction States + +### Button States + +**""** +```css +/* Focus */ +outline: rgb(208, 135, 40) none 3px → rgba(0, 0, 0, 0) dotted 0px; +``` + +### Link Hover + +```css +text-decoration: none → underline; +``` + +## Accessibility (WCAG 2.1) + +**Overall Score: 100%** — 0 passing, 0 failing color pairs + +## Design System Score + +**Overall: 85/100 (Grade: B)** + +| Category | Score | +|----------|-------| +| Color Discipline | 100/100 | +| Typography Consistency | 50/100 | +| Spacing System | 100/100 | +| Shadow Consistency | 100/100 | +| Border Radius Consistency | 90/100 | +| Accessibility | 100/100 | +| CSS Tokenization | 100/100 | + +**Strengths:** Tight, disciplined color palette, Well-defined spacing scale, Clean elevation system, Consistent border radii, Strong accessibility compliance, Good CSS variable tokenization + +**Issues:** +- 4 font families — consider limiting to 2 (heading + body) +- 140 !important rules — prefer specificity over overrides +- 81% of CSS is unused — consider purging +- 6545 duplicate CSS declarations + +## Z-Index Map + +**35 unique z-index values** across 3 layers. + +| Layer | Range | Elements | +|-------|-------|----------| +| modal | 1503,2105 | div.b.u.b.b.l.e.-.e.l.e.m.e.n.t. .C.u.s.t.o.m.E.l.e.m.e.n.t. .b.a.T.a.K.a.Q.a.X. .f.l.o.a.t.i.n.g.-.g.r.o.u.p. .b.u.b.b.l.e.-.r.-.c.o.n.t.a.i.n.e.r. .f.l.e.x. .c.o.l.u.m.n, div.b.u.b.b.l.e.-.e.l.e.m.e.n.t. .C.u.s.t.o.m.E.l.e.m.e.n.t. .b.a.T.a.K.a.Q.a.X. .f.l.o.a.t.i.n.g.-.g.r.o.u.p. .b.u.b.b.l.e.-.r.-.c.o.n.t.a.i.n.e.r. .f.l.e.x. .c.o.l.u.m.n, div.b.u.b.b.l.e.-.e.l.e.m.e.n.t. .C.u.s.t.o.m.E.l.e.m.e.n.t. .b.a.T.a.K.a.Q.a.X. .f.l.o.a.t.i.n.g.-.g.r.o.u.p. .b.u.b.b.l.e.-.r.-.c.o.n.t.a.i.n.e.r. .f.l.e.x. .c.o.l.u.m.n | +| sticky | 10,30 | div.b.u.b.b.l.e.-.e.l.e.m.e.n.t. .G.r.o.u.p. .b.a.T.a.N.a.I.d. .b.u.b.b.l.e.-.r.-.c.o.n.t.a.i.n.e.r. .f.l.e.x. .r.o.w, div.b.u.b.b.l.e.-.e.l.e.m.e.n.t. .T.e.x.t. .b.a.T.a.N.a.I.q, textarea.b.u.b.b.l.e.-.e.l.e.m.e.n.t. .M.u.l.t.i.L.i.n.e.I.n.p.u.t. .a.1.7.8.6.2.3.1.7.9.1.3.3.1.x.5.4.4.4 | +| base | 0,9 | div.b.u.b.b.l.e.-.r.-.c.o.n.t.a.i.n.e.r. .f.l.e.x. .c.o.l.u.m.n, div.b.u.b.b.l.e.-.r.-.c.o.n.t.a.i.n.e.r. .f.l.e.x. .c.o.l.u.m.n, div.b.u.b.b.l.e.-.r.-.c.o.n.t.a.i.n.e.r. .f.l.e.x. .c.o.l.u.m.n | + +## SVG Icons + +**7 unique SVG icons** detected. Dominant style: **filled**. + +| Size Class | Count | +|------------|-------| +| md | 4 | +| lg | 2 | +| xl | 1 | + +**Icon colors:** `rgb(0, 0, 0)` + +## Font Files + +| Family | Source | Weights | Styles | +|--------|--------|---------|--------| +| FontAwesome | self-hosted | normal | normal | +| Material Icons | self-hosted | 400 | normal | +| Material Icons Outline | self-hosted | 400 | normal | +| Inter | google-fonts | 300, 400, 500, 600, 700 | normal | + +**Google Fonts URL:** `https://fonts.googleapis.com/css?family=Inter:300%7CInter:regular%7CInter:500%7CInter:600%7CInter:700` + +## Image Style Patterns + +| Pattern | Count | Key Styles | +|---------|-------|------------| +| thumbnail | 28 | objectFit: contain, borderRadius: 8px, shape: rounded | +| general | 1 | objectFit: cover, borderRadius: 20px, shape: pill | + +**Aspect ratios:** 1:1 (28x), 2:3 (1x) + +## Motion Language + +**Feel:** mixed · **Scroll-linked:** yes + +### Duration Tokens + +| name | value | ms | +|---|---|---| +| `sm` | `250ms` | 250 | + +## Component Anatomy + +### button — 56 instances + +**Slots:** label, icon + +## Brand Voice + +**Tone:** neutral · **Pronoun:** third-person · **Headings:** unknown (tight) + +### Top CTA Verbs + +- **content** (25) +- **favorite** (25) +- **format** (1) +- **filter** (1) +- **add** (1) +- **arrow** (1) +- **note** (1) + +### Button Copy Patterns + +- "content_copy" (25×) +- "favorite_border" (25×) +- "format_line_spacing" (1×) +- "filter_list" (1×) +- "add_circle_outline" (1×) +- "arrow_drop_down" (1×) +- "note_alt" (1×) + +## Page Intent + +**Type:** `landing` (confidence 0.45) +**Description:** A tool to brainstorm, discuss, refine implement and promote ideas. +A social network for building stuff and influencing how stuff is built. +A marketplace for challenges, methods and solutions. +A platfo + +## Material Language + +**Label:** `flat` (confidence 0) + +| Metric | Value | +|--------|-------| +| Avg saturation | 0.101 | +| Shadow profile | soft | +| Avg shadow blur | 0px | +| Max radius | 20px | +| backdrop-filter in use | no | +| Gradients | 0 | + +## Imagery Style + +**Label:** `mixed` (confidence 0) +**Counts:** total 29, svg 0, icon 1, screenshot-like 0, photo-like 0 +**Dominant aspect:** square-ish +**Radius profile on images:** soft + +## Quick Start + +To recreate this design in a new project: + +1. **Install fonts:** Add `Helvetica` from Google Fonts or your font provider +2. **Import CSS variables:** Copy `variables.css` into your project +3. **Tailwind users:** Use the generated `tailwind.config.js` to extend your theme +4. **Design tokens:** Import `design-tokens.json` for tooling integration diff --git a/docs/design-extract/coulomb-social-design-tokens.json b/docs/design-extract/coulomb-social-design-tokens.json new file mode 100644 index 0000000..2cf05ec --- /dev/null +++ b/docs/design-extract/coulomb-social-design-tokens.json @@ -0,0 +1,218 @@ +{ + "$metadata": { + "generator": "designlang", + "version": "7.0.0", + "spec": "https://design-tokens.github.io/community-group/format/", + "source": "https://coulomb.social", + "generatedAt": "2026-08-08T23:30:13.743Z" + }, + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#d08728", + "$type": "color" + } + }, + "neutral": { + "n100": { + "$value": "#000000", + "$type": "color" + }, + "n200": { + "$value": "#efefef", + "$type": "color" + }, + "n300": { + "$value": "#616161", + "$type": "color" + }, + "n400": { + "$value": "#373737", + "$type": "color" + }, + "n500": { + "$value": "#171717", + "$type": "color" + }, + "n600": { + "$value": "#ffffff", + "$type": "color" + }, + "n700": { + "$value": "#e5e5e5", + "$type": "color" + } + }, + "background": { + "bg0": { + "$value": "#ffffff", + "$type": "color" + }, + "bg1": { + "$value": "#e5e5e5", + "$type": "color" + }, + "bg2": { + "$value": "#171717", + "$type": "color" + }, + "bg3": { + "$value": "#f5f5f5", + "$type": "color" + } + }, + "text": { + "text0": { + "$value": "#000000", + "$type": "color" + }, + "text1": { + "$value": "#171717", + "$type": "color" + }, + "text2": { + "$value": "#373737", + "$type": "color" + }, + "text3": { + "$value": "#616161", + "$type": "color" + }, + "text4": { + "$value": "#ffffff", + "$type": "color" + } + } + }, + "spacing": { + "s0": { + "$value": "1px", + "$type": "dimension" + }, + "s1": { + "$value": "24px", + "$type": "dimension" + }, + "s2": { + "$value": "32px", + "$type": "dimension" + }, + "s3": { + "$value": "40px", + "$type": "dimension" + }, + "s4": { + "$value": "60px", + "$type": "dimension" + }, + "s5": { + "$value": "80px", + "$type": "dimension" + }, + "s6": { + "$value": "96px", + "$type": "dimension" + }, + "s7": { + "$value": "320px", + "$type": "dimension" + } + }, + "radius": { + "r0": { + "$value": "4px", + "$type": "dimension" + }, + "r1": { + "$value": "8px", + "$type": "dimension" + }, + "r2": { + "$value": "12px", + "$type": "dimension" + }, + "r3": { + "$value": "15px", + "$type": "dimension" + }, + "r4": { + "$value": "20px", + "$type": "dimension" + } + }, + "shadow": { + "sh0": { + "$value": "rgb(170, 170, 170) 2px 2px 4px 0px", + "$type": "shadow" + }, + "sh1": { + "$value": "rgba(23, 23, 23, 0.1) 2px 2px 8px 2px", + "$type": "shadow" + } + }, + "fontFamily": { + "f0": { + "$value": "Helvetica", + "$type": "fontFamily" + }, + "f1": { + "$value": "Arial", + "$type": "fontFamily" + }, + "f2": { + "$value": "Times New Roman", + "$type": "fontFamily" + }, + "f3": { + "$value": "Inter", + "$type": "fontFamily" + } + } + }, + "semantic": { + "color": { + "action": { + "primary": { + "$value": "{primitive.color.brand.primary}", + "$type": "color" + } + }, + "surface": { + "default": { + "$value": "{primitive.color.background.bg0}", + "$type": "color" + } + }, + "text": { + "body": { + "$value": "{primitive.color.text.text0}", + "$type": "color" + } + } + }, + "typography": { + "body": { + "$value": { + "fontFamily": "Helvetica", + "fontSize": "50px", + "fontWeight": "400", + "lineHeight": "50px" + }, + "$type": "typography" + } + }, + "radius": { + "control": { + "$value": "{primitive.radius.r0}", + "$type": "dimension" + } + }, + "shadow": { + "elevated": { + "$value": "{primitive.shadow.sh0}", + "$type": "shadow" + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-figma-variables.json b/docs/design-extract/coulomb-social-figma-variables.json new file mode 100644 index 0000000..2e8036e --- /dev/null +++ b/docs/design-extract/coulomb-social-figma-variables.json @@ -0,0 +1,449 @@ +{ + "collections": [ + { + "name": "Brand", + "modes": [ + "light" + ], + "variables": [ + { + "name": "color/primary", + "type": "COLOR", + "values": { + "light": { + "r": 0.8156862745098039, + "g": 0.5294117647058824, + "b": 0.1568627450980392, + "a": 1 + } + } + }, + { + "name": "color/neutral/50", + "type": "COLOR", + "values": { + "light": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + } + } + }, + { + "name": "color/neutral/100", + "type": "COLOR", + "values": { + "light": { + "r": 0.9372549019607843, + "g": 0.9372549019607843, + "b": 0.9372549019607843, + "a": 1 + } + } + }, + { + "name": "color/neutral/200", + "type": "COLOR", + "values": { + "light": { + "r": 0.3803921568627451, + "g": 0.3803921568627451, + "b": 0.3803921568627451, + "a": 1 + } + } + }, + { + "name": "color/neutral/300", + "type": "COLOR", + "values": { + "light": { + "r": 0.21568627450980393, + "g": 0.21568627450980393, + "b": 0.21568627450980393, + "a": 1 + } + } + }, + { + "name": "color/neutral/400", + "type": "COLOR", + "values": { + "light": { + "r": 0.09019607843137255, + "g": 0.09019607843137255, + "b": 0.09019607843137255, + "a": 1 + } + } + }, + { + "name": "color/neutral/500", + "type": "COLOR", + "values": { + "light": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + } + } + }, + { + "name": "color/neutral/600", + "type": "COLOR", + "values": { + "light": { + "r": 0.8980392156862745, + "g": 0.8980392156862745, + "b": 0.8980392156862745, + "a": 1 + } + } + }, + { + "name": "color/background/default", + "type": "COLOR", + "values": { + "light": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + } + } + }, + { + "name": "color/background/1", + "type": "COLOR", + "values": { + "light": { + "r": 0.8980392156862745, + "g": 0.8980392156862745, + "b": 0.8980392156862745, + "a": 1 + } + } + }, + { + "name": "color/background/2", + "type": "COLOR", + "values": { + "light": { + "r": 0.09019607843137255, + "g": 0.09019607843137255, + "b": 0.09019607843137255, + "a": 1 + } + } + }, + { + "name": "color/background/3", + "type": "COLOR", + "values": { + "light": { + "r": 0.9607843137254902, + "g": 0.9607843137254902, + "b": 0.9607843137254902, + "a": 1 + } + } + }, + { + "name": "color/text/default", + "type": "COLOR", + "values": { + "light": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + } + } + }, + { + "name": "color/text/1", + "type": "COLOR", + "values": { + "light": { + "r": 0.09019607843137255, + "g": 0.09019607843137255, + "b": 0.09019607843137255, + "a": 1 + } + } + }, + { + "name": "color/text/2", + "type": "COLOR", + "values": { + "light": { + "r": 0.21568627450980393, + "g": 0.21568627450980393, + "b": 0.21568627450980393, + "a": 1 + } + } + }, + { + "name": "color/text/3", + "type": "COLOR", + "values": { + "light": { + "r": 0.3803921568627451, + "g": 0.3803921568627451, + "b": 0.3803921568627451, + "a": 1 + } + } + }, + { + "name": "color/text/4", + "type": "COLOR", + "values": { + "light": { + "r": 1, + "g": 1, + "b": 1, + "a": 1 + } + } + } + ] + }, + { + "name": "Typography", + "modes": [ + "default" + ], + "variables": [ + { + "name": "font/size/50", + "type": "FLOAT", + "values": { + "default": 50 + } + }, + { + "name": "font/weight/50", + "type": "FLOAT", + "values": { + "default": 400 + } + }, + { + "name": "font/lineHeight/50", + "type": "FLOAT", + "values": { + "default": 50 + } + }, + { + "name": "font/size/42", + "type": "FLOAT", + "values": { + "default": 42 + } + }, + { + "name": "font/weight/42", + "type": "FLOAT", + "values": { + "default": 700 + } + }, + { + "name": "font/size/24", + "type": "FLOAT", + "values": { + "default": 24 + } + }, + { + "name": "font/weight/24", + "type": "FLOAT", + "values": { + "default": 600 + } + }, + { + "name": "font/lineHeight/24", + "type": "FLOAT", + "values": { + "default": 36 + } + }, + { + "name": "font/size/18", + "type": "FLOAT", + "values": { + "default": 18 + } + }, + { + "name": "font/weight/18", + "type": "FLOAT", + "values": { + "default": 600 + } + }, + { + "name": "font/lineHeight/18", + "type": "FLOAT", + "values": { + "default": 18 + } + }, + { + "name": "font/size/16", + "type": "FLOAT", + "values": { + "default": 16 + } + }, + { + "name": "font/weight/16", + "type": "FLOAT", + "values": { + "default": 400 + } + }, + { + "name": "font/size/14", + "type": "FLOAT", + "values": { + "default": 14 + } + }, + { + "name": "font/weight/14", + "type": "FLOAT", + "values": { + "default": 500 + } + }, + { + "name": "font/lineHeight/14", + "type": "FLOAT", + "values": { + "default": 19.6 + } + }, + { + "name": "font/size/13.3333", + "type": "FLOAT", + "values": { + "default": 13.3333 + } + }, + { + "name": "font/weight/13.3333", + "type": "FLOAT", + "values": { + "default": 400 + } + } + ] + }, + { + "name": "Spacing", + "modes": [ + "default" + ], + "variables": [ + { + "name": "spacing/1", + "type": "FLOAT", + "values": { + "default": 1 + } + }, + { + "name": "spacing/24", + "type": "FLOAT", + "values": { + "default": 24 + } + }, + { + "name": "spacing/32", + "type": "FLOAT", + "values": { + "default": 32 + } + }, + { + "name": "spacing/40", + "type": "FLOAT", + "values": { + "default": 40 + } + }, + { + "name": "spacing/60", + "type": "FLOAT", + "values": { + "default": 60 + } + }, + { + "name": "spacing/80", + "type": "FLOAT", + "values": { + "default": 80 + } + }, + { + "name": "spacing/96", + "type": "FLOAT", + "values": { + "default": 96 + } + }, + { + "name": "spacing/320", + "type": "FLOAT", + "values": { + "default": 320 + } + }, + { + "name": "radius/sm", + "type": "FLOAT", + "values": { + "default": 4 + } + }, + { + "name": "radius/md", + "type": "FLOAT", + "values": { + "default": 8 + } + }, + { + "name": "radius/lg", + "type": "FLOAT", + "values": { + "default": 12 + } + }, + { + "name": "radius/lg", + "type": "FLOAT", + "values": { + "default": 15 + } + }, + { + "name": "radius/xl", + "type": "FLOAT", + "values": { + "default": 20 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-form-states.json b/docs/design-extract/coulomb-social-form-states.json new file mode 100644 index 0000000..b19b1de --- /dev/null +++ b/docs/design-extract/coulomb-social-form-states.json @@ -0,0 +1,23 @@ +{ + "flags": [], + "forms": { + "count": 0, + "families": {} + }, + "inputTypesSeen": { + "textarea": 10, + "input": 6 + }, + "modals": [], + "toastLibraries": [], + "loading": { + "skeleton": 0, + "spinner": 0 + }, + "empty": { + "count": 0 + }, + "error": { + "count": 0 + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-gradients.css b/docs/design-extract/coulomb-social-gradients.css new file mode 100644 index 0000000..5382e28 --- /dev/null +++ b/docs/design-extract/coulomb-social-gradients.css @@ -0,0 +1,2 @@ +/* designlang — no gradients detected on the source page. */ +/* Run with --interactions to capture hover backgrounds. */ diff --git a/docs/design-extract/coulomb-social-gradients.json b/docs/design-extract/coulomb-social-gradients.json new file mode 100644 index 0000000..8779877 --- /dev/null +++ b/docs/design-extract/coulomb-social-gradients.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://designlang.app/schemas/gradients.json", + "source": "https://coulomb.social", + "generated": "2026-08-08T23:30:22.006Z", + "count": 0, + "gradients": [] +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-icon-system.json b/docs/design-extract/coulomb-social-icon-system.json new file mode 100644 index 0000000..a02fed2 --- /dev/null +++ b/docs/design-extract/coulomb-social-icon-system.json @@ -0,0 +1,199 @@ +{ + "library": "unknown", + "confidence": 0, + "alternates": [], + "stats": { + "count": 55, + "strokeOnly": 0, + "fillOnly": 55, + "mixed": 0, + "avgStrokeWidth": 0, + "gridDistribution": {}, + "roundedCapsFraction": 0 + }, + "signals": [ + "fillDominant" + ], + "icons": [ + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + }, + { + "class": "", + "grid": null, + "strokeWidth": null, + "style": "fill" + } + ] +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-intent.json b/docs/design-extract/coulomb-social-intent.json new file mode 100644 index 0000000..4f54168 --- /dev/null +++ b/docs/design-extract/coulomb-social-intent.json @@ -0,0 +1,24 @@ +{ + "pageIntent": { + "type": "landing", + "confidence": 0.45, + "path": "/", + "title": "Research | Coulomb", + "description": "A tool to brainstorm, discuss, refine implement and promote ideas.\nA social network for building stuff and influencing how stuff is built.\nA marketplace for challenges, methods and solutions.\nA platfo", + "signals": [ + { + "kind": "url", + "type": "landing", + "weight": 0.45, + "detail": "root-path" + } + ], + "alternates": [], + "needsSmart": true + }, + "sectionRoles": { + "sections": [], + "counts": {}, + "readingOrder": [] + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-library.json b/docs/design-extract/coulomb-social-library.json new file mode 100644 index 0000000..4fc4ffb --- /dev/null +++ b/docs/design-extract/coulomb-social-library.json @@ -0,0 +1,12 @@ +{ + "library": "unknown", + "confidence": 0, + "evidence": [], + "alternates": [], + "signals": { + "tailwindLike": 0.136, + "radixAttrCount": 0, + "classSampleSize": 467 + }, + "needsSmart": true +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-mcp.json b/docs/design-extract/coulomb-social-mcp.json new file mode 100644 index 0000000..2bfad0e --- /dev/null +++ b/docs/design-extract/coulomb-social-mcp.json @@ -0,0 +1,275 @@ +{ + "colors": { + "all": [ + { + "hex": "#000000", + "rgb": { + "r": 0, + "g": 0, + "b": 0, + "a": 1 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 0 + }, + "count": 5304, + "contexts": [ + "text", + "border", + "background" + ] + }, + { + "hex": "#efefef", + "rgb": { + "r": 239, + "g": 239, + "b": 239, + "a": 1 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 94 + }, + "count": 610, + "contexts": [ + "background" + ] + }, + { + "hex": "#616161", + "rgb": { + "r": 97, + "g": 97, + "b": 97, + "a": 1 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 38 + }, + "count": 556, + "contexts": [ + "text", + "border" + ] + }, + { + "hex": "#373737", + "rgb": { + "r": 55, + "g": 55, + "b": 55, + "a": 1 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 22 + }, + "count": 462, + "contexts": [ + "text", + "border" + ] + }, + { + "hex": "#171717", + "rgb": { + "r": 23, + "g": 23, + "b": 23, + "a": 1 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 9 + }, + "count": 401, + "contexts": [ + "text", + "border", + "background" + ] + }, + { + "hex": "#ffffff", + "rgb": { + "r": 255, + "g": 255, + "b": 255, + "a": 1 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 100 + }, + "count": 242, + "contexts": [ + "background", + "border", + "text" + ] + }, + { + "hex": "#e5e5e5", + "rgb": { + "r": 229, + "g": 229, + "b": 229, + "a": 1 + }, + "hsl": { + "h": 0, + "s": 0, + "l": 90 + }, + "count": 217, + "contexts": [ + "border", + "background" + ] + }, + { + "hex": "#d08728", + "rgb": { + "r": 208, + "g": 135, + "b": 40, + "a": 1 + }, + "hsl": { + "h": 34, + "s": 68, + "l": 49 + }, + "count": 8, + "contexts": [ + "background" + ] + } + ] + }, + "regions": [], + "componentClusters": [ + { + "kind": "button", + "structuralHash": "button>svg", + "instanceCount": 55, + "variants": [ + { + "css": { + "background": "rgba(0, 0, 0, 0)", + "color": "rgb(55, 55, 55)", + "padding": "0px 0px 0px 0px", + "borderRadius": "4px", + "border": "0px none rgb(55, 55, 55)", + "fontSize": "13.3333px", + "fontWeight": "400" + }, + "instanceCount": 55 + } + ] + }, + { + "kind": "button", + "structuralHash": "button", + "instanceCount": 1, + "variants": [ + { + "css": { + "background": "rgb(239, 239, 239)", + "color": "rgb(0, 0, 0)", + "padding": "1px 6px 1px 6px", + "borderRadius": "0px", + "border": "2px outset rgb(0, 0, 0)", + "fontSize": "13.3333px", + "fontWeight": "400" + }, + "instanceCount": 1 + } + ] + } + ], + "accessibility": { + "remediation": [] + }, + "cssHealth": { + "sheets": [ + { + "url": "https://fonts.googleapis.com/css?family=Inter:300%7CInter:regular%7CInter:500%7CInter:600%7CInter:700", + "totalBytes": 11585, + "usedBytes": 0, + "unusedBytes": 11585, + "unusedPercent": 100 + }, + { + "url": "https://coulomb.social/package/run_css/0591b05f69514365ec03deea8134e9c2ff462a44dd0474f9fb551cb47e38e201/coulomb/live/vw_pages/xfalse/xfalse/run.css", + "totalBytes": 228282, + "usedBytes": 45959, + "unusedBytes": 182323, + "unusedPercent": 80 + }, + { + "url": "https://coulomb.social/vw_pages?resume=1786231781137x912895168994024000", + "totalBytes": 821, + "usedBytes": 0, + "unusedBytes": 821, + "unusedPercent": 100 + }, + { + "url": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/styles/default.min.css", + "totalBytes": 1144, + "usedBytes": 0, + "unusedBytes": 1144, + "unusedPercent": 100 + } + ], + "totalBytes": 241832, + "usedBytes": 45959, + "unusedBytes": 195873, + "unusedPercent": 81, + "importantCount": 140, + "duplicates": 6545, + "vendorPrefixes": { + "webkit": 139, + "moz": 436, + "ms": 96, + "o": 34 + }, + "keyframes": [ + { + "name": "highlight-in", + "steps": 1 + }, + { + "name": "fa-spin", + "steps": 2 + } + ], + "specificity": { + "max": [ + 1, + 7, + 46 + ], + "average": [ + 0, + 1.84, + 0.38 + ], + "count": 2001 + }, + "issues": [ + "140 !important rules", + "6545 duplicate declarations", + "81% unused CSS" + ] + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-motion-tokens.json b/docs/design-extract/coulomb-social-motion-tokens.json new file mode 100644 index 0000000..357ae54 --- /dev/null +++ b/docs/design-extract/coulomb-social-motion-tokens.json @@ -0,0 +1,16 @@ +{ + "$description": "Motion tokens extracted by designlang", + "duration": { + "sm": { + "$value": "250ms", + "$type": "duration", + "ms": 250 + } + }, + "easing": {}, + "spring": {}, + "$meta": { + "feel": "mixed", + "scrollLinked": true + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-motion.css b/docs/design-extract/coulomb-social-motion.css new file mode 100644 index 0000000..349be56 --- /dev/null +++ b/docs/design-extract/coulomb-social-motion.css @@ -0,0 +1,38 @@ +/* Motion CSS — generated by designlang (motionlang) + * Source: https://coulomb.social + * 2026-08-08T23:30:22.011Z + * + * + *
…
+ */ + +:root { + --duration-sm: 250ms; + --ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1); +} + +@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } +@keyframes slide-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } } +@keyframes scale-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: none; } } +@keyframes pop { 0% { transform: scale(0.9); } 60% { transform: scale(1.03); } 100% { transform: scale(1); } } + +.mo-animate, +[class^="mo-"], [class*=" mo-"] { + animation-duration: var(--duration-sm, 250ms); + animation-timing-function: var(--ease-standard); + animation-fill-mode: both; +} + +.mo-fade-in { animation-name: fade-in; } +.mo-slide-up { animation-name: slide-up; } +.mo-scale-in { animation-name: scale-in; } +.mo-pop { animation-name: pop; animation-timing-function: var(--ease-standard); } + +@media (prefers-reduced-motion: reduce) { + *, ::before, ::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/docs/design-extract/coulomb-social-motion.framer.js b/docs/design-extract/coulomb-social-motion.framer.js new file mode 100644 index 0000000..fb6691c --- /dev/null +++ b/docs/design-extract/coulomb-social-motion.framer.js @@ -0,0 +1,70 @@ +// Framer Motion presets — generated by designlang (motionlang) +// Source: https://coulomb.social +// 2026-08-08T23:30:22.009Z +// +// import { transitions, variants } from './coulomb.social-motion.framer'; +// + +/** Easing curves extracted from the live page, as Framer cubic-bezier arrays. */ +export const easings = { + standard: [0.25, 1, 0.5, 1], +}; + +/** Duration presets (seconds), extracted from the live page. */ +export const durations = { + sm: 0.25, +}; + +/** Spring presets — pass to a Framer Motion `transition` prop. */ +export const springs = { + soft: { type: 'spring', stiffness: 320, damping: 30, mass: 1 }, +}; + +/** Ready-to-spread Framer Motion transition objects. */ +export const transitions = { + base: { duration: 0.25, ease: easings.standard }, + fast: { duration: 0.125, ease: easings.standard }, + slow: { duration: 0.450, ease: easings.standard }, + spring: springs.soft, +}; + +/** Common variants wired to the extracted timing. */ +export const variants = { + fade: { + hidden: { opacity: 0 }, + show: { opacity: 1, transition: transitions.base }, + }, + slideUp: { + hidden: { opacity: 0, y: 16 }, + show: { opacity: 1, y: 0, transition: transitions.base }, + }, + scaleIn: { + hidden: { opacity: 0, scale: 0.96 }, + show: { opacity: 1, scale: 1, transition: transitions.base }, + }, + pop: { + hidden: { opacity: 0, scale: 0.9 }, + show: { opacity: 1, scale: 1, transition: springs.soft }, + }, + stagger: { + hidden: {}, + show: { transition: { staggerChildren: 0.063 } }, + }, +}; + +/** Site uses scroll- or view-timeline. Drop-in `whileInView` props. */ +export const inView = { + fadeIn: { + initial: variants.fade.hidden, + whileInView: variants.fade.show, + viewport: { once: true, amount: 0.3 }, + }, + riseIn: { + initial: variants.slideUp.hidden, + whileInView: variants.slideUp.show, + viewport: { once: true, amount: 0.3 }, + }, +}; + +export default { easings, durations, springs, transitions, variants, inView }; diff --git a/docs/design-extract/coulomb-social-motion.gsap.js b/docs/design-extract/coulomb-social-motion.gsap.js new file mode 100644 index 0000000..a7c884e --- /dev/null +++ b/docs/design-extract/coulomb-social-motion.gsap.js @@ -0,0 +1,49 @@ +// GSAP presets — generated by designlang (motionlang) +// Source: https://coulomb.social +// 2026-08-08T23:30:22.015Z +// +// import gsap from 'gsap'; +// import { registerEases, reveals } from './coulomb.social-motion.gsap.js'; +// registerEases(gsap); // one-time: registers CustomEases +// reveals.slideUp('.card'); + +/** CustomEase path data extracted from the live page, keyed by family. */ +export const eases = { + standard: 'M0,0 C0.25,0.1 0.25,1 1,1', +}; + +/** Duration presets (seconds), extracted from the live page. */ +export const durations = { + sm: 0.25, +}; + +/** Register every extracted curve as a named GSAP CustomEase. Call once. */ +export function registerEases(gsap) { + const CustomEase = gsap.plugins?.CustomEase || (typeof window !== 'undefined' && window.CustomEase); + if (!CustomEase) { + console.warn('[motion.gsap] CustomEase plugin not registered — call gsap.registerPlugin(CustomEase) first.'); + return; + } + for (const [name, path] of Object.entries(eases)) CustomEase.create(name, path); +} + +/** gsap.from() reveal helpers. Pass any GSAP target + optional vars override. */ +export const reveals = { + fadeIn: (target, vars = {}) => gsap.from(target, { opacity: 0, duration: 0.25, ease: 'standard', ...vars }), + slideUp: (target, vars = {}) => gsap.from(target, { opacity: 0, y: 24, duration: 0.25, ease: 'standard', ...vars }), + slideDown: (target, vars = {}) => gsap.from(target, { opacity: 0, y: -24, duration: 0.25, ease: 'standard', ...vars }), + scaleIn: (target, vars = {}) => gsap.from(target, { opacity: 0, scale: 0.96, duration: 0.25, ease: 'standard', ...vars }), + pop: (target, vars = {}) => gsap.from(target, { scale: 0.9, duration: 0.25, ease: 'standard', ...vars }), +}; + +/** Site uses scroll-/view-timeline. Batch-reveals elements as they enter. */ +/** Requires: gsap.registerPlugin(ScrollTrigger). */ +export function revealOnScroll(targets, vars = {}) { + return gsap.utils.toArray(targets).map((el) => + gsap.from(el, { + opacity: 0, y: 24, duration: 0.25, ease: 'standard', ...vars, + scrollTrigger: { trigger: el, start: 'top 85%', toggleActions: 'play none none reverse' }, + })); +} + +export default { eases, durations, registerEases, reveals }; diff --git a/docs/design-extract/coulomb-social-motion.html b/docs/design-extract/coulomb-social-motion.html new file mode 100644 index 0000000..bbeef60 --- /dev/null +++ b/docs/design-extract/coulomb-social-motion.html @@ -0,0 +1,138 @@ + + + + + +coulomb.social — motion lab · designlang + + + +
+
+

motionlang · motion lab

+

coulomb.social in motion

+

Every easing curve, duration and keyframe animation designlang read off the live coulomb.social page — drawn, timed and replayed. Generated 2026-08-08.

+
+ +

Easing curves

+
No easing curves detected. Re-run with --interactions to capture hover transitions.
+ +

Durations

+
+
+ sm + + 250ms +
+ +

Keyframe animations

+ +
+
@keyframes highlight-in
+

+    
+
+
@keyframes fa-spin
+

+    
+
+
@keyframes opacity-70-25-0-13
+

+    
+
+
@keyframes opacity-70-25-1-13
+

+    
+
+
@keyframes opacity-70-25-2-13
+

+    
+
+
@keyframes opacity-70-25-3-13
+

+    
+
+
@keyframes opacity-70-25-4-13
+

+    
+
+
@keyframes opacity-70-25-5-13
+

+    
+
+
@keyframes opacity-70-25-6-13
+

+    
+
+
@keyframes opacity-70-25-7-13
+

+    
+
+
@keyframes opacity-70-25-8-13
+

+    
+
+
@keyframes opacity-70-25-9-13
+

+    
+ +
+ Generated by designlang · motionlang · designlang.app · re-extract with npx designlang coulomb.social --motion +
+
+ + \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-motion.one.js b/docs/design-extract/coulomb-social-motion.one.js new file mode 100644 index 0000000..4749f54 --- /dev/null +++ b/docs/design-extract/coulomb-social-motion.one.js @@ -0,0 +1,58 @@ +// Motion One presets — generated by designlang (motionlang) +// Source: https://coulomb.social +// 2026-08-08T23:30:22.010Z +// +// import { animate } from 'motion'; +// import { animations, easings, durations } from './coulomb.social-motion.one.js'; +// +// animations.fadeIn('#hero'); +// animations.slideUp('.card', { delay: 0.1 }); + +/** Easing curves extracted from the live page, as Motion One cubic-bezier arrays. */ +export const easings = { + standard: [0.25, 0.1, 0.25, 1], +}; + +/** Duration presets (seconds), extracted from the live page. */ +export const durations = { + sm: 0.25, +}; + +/** Motion One spring options — pass as the `type: 'spring'` block of an animate() call. */ +export const springs = { + soft: { type: 'spring', stiffness: 320, damping: 30, mass: 1 }, +}; + +/** No on-page @keyframes were attached to elements; emit empty for shape parity. */ +export const keyframes = {}; + +/** Animate-on-call helpers. Each takes a Motion One target + optional override. */ +import { animate } from 'motion'; + +const _t = { duration: 0.25, easing: easings.standard }; + +export const animations = { + fadeIn: (target, opts = {}) => + animate(target, { opacity: [0, 1] }, { ..._t, ...opts }), + fadeOut: (target, opts = {}) => + animate(target, { opacity: [1, 0] }, { ..._t, ...opts }), + slideUp: (target, opts = {}) => + animate(target, { opacity: [0, 1], y: [16, 0] }, { ..._t, ...opts }), + slideDown: (target, opts = {}) => + animate(target, { opacity: [0, 1], y: [-16, 0] }, { ..._t, ...opts }), + scaleIn: (target, opts = {}) => + animate(target, { opacity: [0, 1], scale: [0.96, 1] }, { ..._t, ...opts }), + pop: (target, opts = {}) => + animate(target, { scale: [0.9, 1] }, { ...springs.soft, ...opts }), +}; + +/** Site uses scroll- or view-timeline. Helpers wired to Motion One's `inView` / `scroll`. */ +import { inView, scroll } from 'motion'; + +export const enterOnView = (target, variant = animations.slideUp) => + inView(target, (el) => { variant(el.target); }); + +export const parallaxY = (target, distance = 80) => + scroll(animate(target, { y: [0, -distance] }, { easing: 'linear' })); + +export default { easings, durations, springs, keyframes, animations }; diff --git a/docs/design-extract/coulomb-social-motion.tailwind.js b/docs/design-extract/coulomb-social-motion.tailwind.js new file mode 100644 index 0000000..04f0524 --- /dev/null +++ b/docs/design-extract/coulomb-social-motion.tailwind.js @@ -0,0 +1,67 @@ +// Tailwind motion preset — generated by designlang (motionlang) +// Source: https://coulomb.social +// 2026-08-08T23:30:22.014Z +// +// Merge `extend` into your tailwind.config theme.extend, e.g.: +// const motion = require('./coulomb.social-motion.tailwind'); +// module.exports = { theme: { extend: { ...motion.extend } } }; +// +// Then:
or `duration-md ease-spring`. + +const extend = { + "transitionDuration": { + "sm": "250ms" + }, + "transitionTimingFunction": { + "standard": "cubic-bezier(0.25, 0.1, 0.25, 1)" + }, + "keyframes": { + "fade-in": { + "0%": { + "opacity": "0" + }, + "100%": { + "opacity": "1" + } + }, + "slide-up": { + "0%": { + "opacity": "0", + "transform": "translateY(16px)" + }, + "100%": { + "opacity": "1", + "transform": "none" + } + }, + "scale-in": { + "0%": { + "opacity": "0", + "transform": "scale(0.96)" + }, + "100%": { + "opacity": "1", + "transform": "none" + } + }, + "pop": { + "0%": { + "transform": "scale(0.9)" + }, + "60%": { + "transform": "scale(1.03)" + }, + "100%": { + "transform": "scale(1)" + } + } + }, + "animation": { + "fade-in": "fade-in 250ms var(--tw-ease-standard, cubic-bezier(0.25, 0.1, 0.25, 1)) both", + "slide-up": "slide-up 250ms var(--tw-ease-standard, cubic-bezier(0.25, 0.1, 0.25, 1)) both", + "scale-in": "scale-in 250ms var(--tw-ease-standard, cubic-bezier(0.25, 0.1, 0.25, 1)) both", + "pop": "pop 250ms var(--tw-ease-standard, cubic-bezier(0.25, 0.1, 0.25, 1)) both" + } +}; + +module.exports = { extend }; diff --git a/docs/design-extract/coulomb-social-motion.waapi.js b/docs/design-extract/coulomb-social-motion.waapi.js new file mode 100644 index 0000000..ef0b25c --- /dev/null +++ b/docs/design-extract/coulomb-social-motion.waapi.js @@ -0,0 +1,44 @@ +// Web Animations API presets — generated by designlang (motionlang) +// Source: https://coulomb.social +// 2026-08-08T23:30:22.016Z +// +// Zero-dependency. Runs on the browser-native Element.animate(). +// import { animations } from './coulomb.social-motion.waapi.js'; +// animations.fadeIn(document.querySelector('#hero')); +// await animations.slideUp(card, { delay: 80 }).finished; + +/** Easing curves extracted from the live page, as WAAPI easing strings. */ +export const easings = { + standard: 'cubic-bezier(0.25, 0.1, 0.25, 1)', +}; + +/** Duration presets (milliseconds), extracted from the live page. */ +export const durations = { + sm: 250, +}; + +/** No on-page @keyframes were attached to elements; emit empty for shape parity. */ +export const keyframes = {}; + +/** Honours the user's reduced-motion preference. */ +export const prefersReducedMotion = () => + typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches; + +const _timing = { duration: 250, easing: easings.standard, fill: 'both' }; + +function _animate(el, frames, opts = {}) { + const timing = { ..._timing, ...opts }; + if (prefersReducedMotion()) timing.duration = 0; + return el.animate(frames, timing); +} + +/** Animate-on-call helpers. Each returns the live Animation (await `.finished`). */ +export const animations = { + fadeIn: (el, opts = {}) => _animate(el, [{ opacity: 0 }, { opacity: 1 }], opts), + fadeOut: (el, opts = {}) => _animate(el, [{ opacity: 1 }, { opacity: 0 }], opts), + slideUp: (el, opts = {}) => _animate(el, [{ opacity: 0, transform: 'translateY(16px)' }, { opacity: 1, transform: 'translateY(0)' }], opts), + slideDown: (el, opts = {}) => _animate(el, [{ opacity: 0, transform: 'translateY(-16px)' }, { opacity: 1, transform: 'translateY(0)' }], opts), + scaleIn: (el, opts = {}) => _animate(el, [{ opacity: 0, transform: 'scale(0.96)' }, { opacity: 1, transform: 'scale(1)' }], opts), +}; + +export default { easings, durations, keyframes, animations, prefersReducedMotion }; diff --git a/docs/design-extract/coulomb-social-preview.html b/docs/design-extract/coulomb-social-preview.html new file mode 100644 index 0000000..612241e --- /dev/null +++ b/docs/design-extract/coulomb-social-preview.html @@ -0,0 +1,433 @@ + + + + + +Design Language: Research | Coulomb + + + +
+ +
+

Research | Coulomb

+ +
+
+ https://coulomb.social + 3445 elements + 8/9/2026 + 7 pages crawled +
+ +
+
8
Colors
+
4
Font Families
+
8
Spacing Values
+
2
Shadows
+
5
Border Radii
+
5
Components
+
100%
A11y Score
+
+ + +

Color Palette

+ + +

Brand Colors

+
+
+
+
+
#d08728
+
Primary
+
8 uses
+
+
+ + +
+ + +

Neutrals

+
+
+
+
+
#000000
+ +
5304 uses
+
+
+
+
+
+
#efefef
+ +
610 uses
+
+
+
+
+
+
#616161
+ +
556 uses
+
+
+
+
+
+
#373737
+ +
462 uses
+
+
+
+
+
+
#171717
+ +
401 uses
+
+
+
+
+
+
#ffffff
+ +
242 uses
+
+
+
+
+
+
#e5e5e5
+ +
217 uses
+
+
+
+ + +

Full Palette

+
+
+
+
+
#000000
+
text, border, background
+
5304 uses
+
+
+
+
+
+
#efefef
+
background
+
610 uses
+
+
+
+
+
+
#616161
+
text, border
+
556 uses
+
+
+
+
+
+
#373737
+
text, border
+
462 uses
+
+
+
+
+
+
#171717
+
text, border, background
+
401 uses
+
+
+
+
+
+
#ffffff
+
background, border, text
+
242 uses
+
+
+
+
+
+
#e5e5e5
+
border, background
+
217 uses
+
+
+
+
+
+
#d08728
+
background
+
8 uses
+
+
+
+ + + + +

Typography

+ + +

Font Families

+
+ Helvetica (body, 1739x)Arial (body, 761x)Times New Roman (body, 450x)Inter (body, 397x) +
+ + +

Type Scale

+
+ +
+ 50px + The quick brown fox + 400 / 50px +
+
+ 42px + The quick brown fox + 700 / normal +
+
+ 24px + The quick brown fox + 600 / 36px +
+
+ 18px + The quick brown fox + 600 / 18px +
+
+ 16px + The quick brown fox + 400 / normal +
+
+ 14px + The quick brown fox + 500 / 19.6px +
+
+ 13.3333px + The quick brown fox + 400 / normal +
+
+ + + +

Spacing Scale (base: 4px)

+
+ +
+ 1px +
+
+
+ 24px +
+
+
+ 32px +
+
+
+ 40px +
+
+
+ 60px +
+
+
+ 80px +
+
+
+ 96px +
+
+
+ 320px +
+
+
+ + + +

Box Shadows

+
+ +
+ sm +
+
+ md +
+
+ + + +

Border Radii

+
+ +
+
4px
+
sm
+
+
+
8px
+
md
+
+
+
12px
+
lg
+
+
+
15px
+
lg
+
+
+
20px
+
xl
+
+
+ + + +

Accessibility

+
+
100%
+
+
WCAG Contrast Score
+
0 passing / 0 failing color pairs
+
+
+ +

Color Pair Analysis

+ + + + + + +
+ + + \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-prompts/claude-artifacts.md b/docs/design-extract/coulomb-social-prompts/claude-artifacts.md new file mode 100644 index 0000000..b3b9c02 --- /dev/null +++ b/docs/design-extract/coulomb-social-prompts/claude-artifacts.md @@ -0,0 +1,13 @@ +Create a React artifact that reproduces this brand's design language. +Page intent: landing. +Material language: flat. +Voice: Tone: neutral · Headings: unknown · CTA verbs: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object]. +Colors to use: #000000, #efefef, #616161, #373737, #171717, #ffffff, #e5e5e5, #d08728. +Fonts: [object Object], [object Object], [object Object], [object Object]. +Radius vocabulary: 4, 8, 12, 15, 20. +Sections: +- hero +- features +- cta +- footer +Use Tailwind via CDN, lucide-react for icons, and keep the material language consistent across sections. Do not add extra decorative elements outside this vocabulary. \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-prompts/cursor.md b/docs/design-extract/coulomb-social-prompts/cursor.md new file mode 100644 index 0000000..c532f00 --- /dev/null +++ b/docs/design-extract/coulomb-social-prompts/cursor.md @@ -0,0 +1,18 @@ +# Design brief +Page type: **landing**. +Material language: **flat**. +Voice: Tone: neutral · Headings: unknown · CTA verbs: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object]. +## Tokens +```ts +export const tokens = { + colors: ['#000000', '#efefef', '#616161', '#373737', '#171717', '#ffffff', '#e5e5e5', '#d08728'], + fonts: ['[object Object]', '[object Object]', '[object Object]', '[object Object]'], + radii: ['4', '8', '12', '15', '20'], + shadows: ['rgb(170, 170, 170) 2px 2px 4px 0px', 'rgba(23, 23, 23, 0.1) 2px 2px 8px 2px'], +}; +``` +## Sections +- hero +- features +- cta +- footer \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-prompts/lovable.txt b/docs/design-extract/coulomb-social-prompts/lovable.txt new file mode 100644 index 0000000..5569421 --- /dev/null +++ b/docs/design-extract/coulomb-social-prompts/lovable.txt @@ -0,0 +1,12 @@ +Clone the design language of this landing page and build a fresh equivalent. +Visual feel: flat. Tone: neutral · Headings: unknown · CTA verbs: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object] +Primary palette: #000000, #efefef, #616161, #373737, #171717. +Typography: [object Object], [object Object], [object Object], [object Object]. +Corner radius vocabulary: 4, 8, 12, 15, 20. +Shadow vocabulary: rgb(170, 170, 170) 2px 2px 4px 0px | rgba(23, 23, 23, 0.1) 2px 2px 8px 2px. +Page structure: +- hero +- features +- social proof +- cta +- footer \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-prompts/recipe-button.md b/docs/design-extract/coulomb-social-prompts/recipe-button.md new file mode 100644 index 0000000..142496f --- /dev/null +++ b/docs/design-extract/coulomb-social-prompts/recipe-button.md @@ -0,0 +1,28 @@ +# Recipe: button +Build one button component that matches this brand. +Palette: #000000, #efefef, #616161, #373737, #171717, #ffffff +Typography: [object Object], [object Object], [object Object], [object Object] +Material: flat +Signals: Radius: 4, 8, 12, 15, 20 · Shadows: rgb(170, 170, 170) 2px 2px 4px 0px | rgba(23, 23, 23, 0.1) 2px 2px 8px 2px +## Anatomy (detected) +```json +{ + "kind": "button", + "structuralHash": "button", + "instanceCount": 1, + "variants": [ + { + "css": { + "background": "rgb(239, 239, 239)", + "color": "rgb(0, 0, 0)", + "padding": "1px 6px 1px 6px", + "borderRadius": "0px", + "border": "2px outset rgb(0, 0, 0)", + "fontSize": "13.3333px", + "fontWeight": "400" + }, + "instanceCount": 1 + } + ] +} +``` \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-prompts/v0.txt b/docs/design-extract/coulomb-social-prompts/v0.txt new file mode 100644 index 0000000..a5ca0ac --- /dev/null +++ b/docs/design-extract/coulomb-social-prompts/v0.txt @@ -0,0 +1,22 @@ +Build a landing page with this exact visual language. +COLORS: + #000000 + #efefef + #616161 + #373737 + #171717 + #ffffff + #e5e5e5 + #d08728 +FONTS: [object Object], [object Object], [object Object], [object Object] +SPACING: 1, 24, 32, 40, 60, 80, 96, 320 +RADIUS: 4, 8, 12, 15, 20 +SHADOWS: rgb(170, 170, 170) 2px 2px 4px 0px | rgba(23, 23, 23, 0.1) 2px 2px 8px 2px +MATERIAL LANGUAGE: flat +VOICE: Tone: neutral · Headings: unknown · CTA verbs: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object] +SECTIONS (in order): +- hero +- features +- cta +- footer +Use Tailwind. Match these tokens exactly. Keep the material language consistent. \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-reset.css b/docs/design-extract/coulomb-social-reset.css new file mode 100644 index 0000000..b9f0c50 --- /dev/null +++ b/docs/design-extract/coulomb-social-reset.css @@ -0,0 +1,81 @@ +/* ────────────────────────────────────────────────────────────── + Brand-aware reset — generated by designlang + Research | Coulomb + 2026-08-08T23:30:22.005Z + ────────────────────────────────────────────────────────────── */ + +*, *::before, *::after { box-sizing: border-box; } + +html, body, h1, h2, h3, h4, h5, h6, p, ul, ol, li, blockquote, figure, +form, fieldset, table, hr { + margin: 0; + padding: 0; +} + +html { + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + text-size-adjust: 100%; + -webkit-tap-highlight-color: transparent; + font-family: "Helvetica", system-ui, -apple-system, 'Segoe UI', sans-serif; + font-size: 16px; + line-height: 1.5; +} + +body { + background-color: #ffffff; + color: #000000; + font-weight: 400; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Helvetica", system-ui, -apple-system, sans-serif; + font-weight: 600; + line-height: 1.15; + letter-spacing: -0.015em; +} + +a { + color: #d08728; + text-decoration: none; + background-color: transparent; +} +a:hover { text-decoration: underline; } +a:focus-visible { outline: 2px solid #d08728; outline-offset: 2px; border-radius: 4px; } + +img, picture, video, canvas, svg { + display: block; + max-width: 100%; + height: auto; +} + +input, textarea, select, button { + font: inherit; + color: inherit; +} +button { cursor: pointer; background: none; border: 0; padding: 0; } + +code, pre, kbd, samp { + font-family: ui-monospace, ui-monospace, 'SF Mono', Menlo, monospace; +} + +hr { + border: 0; + border-top: 1px solid #e5e5e5; +} + +::selection { + background: #d08728; + color: #ffffff; +} + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.001ms !important; + transition-duration: 0.001ms !important; + scroll-behavior: auto !important; + } +} diff --git a/docs/design-extract/coulomb-social-routes-report.md b/docs/design-extract/coulomb-social-routes-report.md new file mode 100644 index 0000000..6acd239 --- /dev/null +++ b/docs/design-extract/coulomb-social-routes-report.md @@ -0,0 +1,15 @@ +# Multi-route Token Reconciliation + +- Routes crawled: **7** +- Shared tokens: **17** +- Total unique tokens across routes: **41** +- Cross-route drift entries: **148** + +## Per-route contributions +- `/` (index): 0 added, 19 changed +- `/pg_chunk/1786106348475x662489469743530000` (pg-chunk-1786106348475x662489469743530000): 1 added, 22 changed +- `/pg_chunk/1767965374100x901547861536145400` (pg-chunk-1767965374100x901547861536145400): 0 added, 21 changed +- `/pg_chunk/1785501151364x921456713843343400` (pg-chunk-1785501151364x921456713843343400): 0 added, 21 changed +- `/pg_chunk/1784904857383x503270614975381500` (pg-chunk-1784904857383x503270614975381500): 0 added, 22 changed +- `/pg_chunk/1785501127011x363256181345746940` (pg-chunk-1785501127011x363256181345746940): 0 added, 21 changed +- `/pg_chunk/1784305925912x322414739506593800` (pg-chunk-1784305925912x322414739506593800): 0 added, 22 changed diff --git a/docs/design-extract/coulomb-social-seo.json b/docs/design-extract/coulomb-social-seo.json new file mode 100644 index 0000000..4d3fe62 --- /dev/null +++ b/docs/design-extract/coulomb-social-seo.json @@ -0,0 +1,40 @@ +{ + "openGraph": { + "title": "An Ideas Come To Life Community!", + "site_name": "Coulomb Social", + "description": "A tool to brainstorm, discuss, refine implement and promote ideas.\nA social network for building stuff and influencing how stuff is built.\nA marketplace for challenges, methods and solutions.\nA platform to monetize curiosity.", + "image": "https://13a55be71f3a6a4c9ea356833df823c4.cdn.bubble.io/cdn-cgi/image/w=,h=,f=auto,dpr=1,fit=contain/f1750630995632x205964292977919070/Coulomb%20Logo.png", + "url": "https://coulomb.social/vw_pages?resume=1786231781137x912895168994024000", + "type": "website" + }, + "twitter": { + "title": "An Ideas Come To Life Community!", + "site_name": "Coulomb Social", + "description": "A tool to brainstorm, discuss, refine implement and promote ideas.\nA social network for building stuff and influencing how stuff is built.\nA marketplace for challenges, methods and solutions.\nA platform to monetize curiosity.", + "image:src": "https://13a55be71f3a6a4c9ea356833df823c4.cdn.bubble.io/cdn-cgi/image/w=,h=,f=auto,dpr=1,fit=contain/f1750630995632x205964292977919070/Coulomb%20Logo.png", + "card": "summary_large_image" + }, + "description": "A tool to brainstorm, discuss, refine implement and promote ideas.\nA social network for building stuff and influencing how stuff is built.\nA marketplace for challenges, methods and solutions.\nA platform to monetize curiosity.", + "canonical": null, + "themeColor": null, + "viewport": "width=device-width,initial-scale=1.0", + "favicons": [ + { + "rel": "icon", + "href": "https://13a55be71f3a6a4c9ea356833df823c4.cdn.bubble.io/cdn-cgi/image/w=128,h=,f=auto,dpr=1,fit=contain/f1738533145653x421346820735372100/Coulomb%20Logo.png", + "sizes": "", + "type": "image/png" + } + ], + "manifest": null, + "structuredData": [], + "score": { + "hasOg": true, + "hasTwitter": true, + "hasDescription": true, + "hasCanonical": false, + "hasStructuredData": false, + "hasFavicon": true, + "hasThemeColor": false + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-shadcn-theme.css b/docs/design-extract/coulomb-social-shadcn-theme.css new file mode 100644 index 0000000..4dd69bd --- /dev/null +++ b/docs/design-extract/coulomb-social-shadcn-theme.css @@ -0,0 +1,15 @@ +/* shadcn/ui Theme — extracted from https://coulomb.social */ +/* Paste into your globals.css */ + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 0%; + --primary: 34 68% 49%; + --primary-foreground: 0 0% 100%; + --muted: 0 0% 90%; + --muted-foreground: 0 0% 0%; + --border: 0 0% 9%; + --radius: 8px; + } +} diff --git a/docs/design-extract/coulomb-social-stack-intel.json b/docs/design-extract/coulomb-social-stack-intel.json new file mode 100644 index 0000000..2457150 --- /dev/null +++ b/docs/design-extract/coulomb-social-stack-intel.json @@ -0,0 +1,12 @@ +{ + "cms": [ + "webflow", + "bubble" + ], + "analytics": [], + "experimentation": [], + "signals": { + "scriptCount": 10, + "metaCount": 15 + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tailwind-v4.css b/docs/design-extract/coulomb-social-tailwind-v4.css new file mode 100644 index 0000000..2bfe39b --- /dev/null +++ b/docs/design-extract/coulomb-social-tailwind-v4.css @@ -0,0 +1,55 @@ +/* ────────────────────────────────────────────────────────────── + Tailwind v4 @theme — generated by designlang + site: Research | Coulomb + 2026-08-08T23:30:22.003Z + Drop this above `@import "tailwindcss";`. + ────────────────────────────────────────────────────────────── */ + +@import "tailwindcss"; + +@theme { + --color-primary-50: hsl(34 68% 97%); + --color-primary-100: hsl(34 68% 94%); + --color-primary-200: hsl(34 68% 86%); + --color-primary-300: hsl(34 68% 76%); + --color-primary-400: hsl(34 68% 64%); + --color-primary-500: hsl(34 68% 50%); + --color-primary-600: hsl(34 68% 40%); + --color-primary-700: hsl(34 68% 32%); + --color-primary-800: hsl(34 68% 24%); + --color-primary-900: hsl(34 68% 16%); + --color-primary-950: hsl(34 68% 10%); + --color-primary: #d08728; + --color-neutral-50: #000000; + --color-neutral-100: #efefef; + --color-neutral-200: #616161; + --color-neutral-300: #373737; + --color-neutral-400: #171717; + --color-neutral-500: #ffffff; + --color-neutral-600: #e5e5e5; + --color-background: #ffffff; + --color-foreground: #000000; + --font-sans: "Helvetica", system-ui, sans-serif; + --spacing-1: 1px; + --spacing-2: 24px; + --spacing-3: 32px; + --spacing-4: 40px; + --spacing-5: 60px; + --spacing-6: 80px; + --spacing-7: 96px; + --spacing-8: 320px; + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 15px; + --radius-2xl: 20px; + --shadow-sm: rgb(170, 170, 170) 2px 2px 4px 0px; + --shadow-md: rgba(23, 23, 23, 0.1) 2px 2px 8px 2px; + --animate-duration-fast: undefined; +} + +/* Optional base reset that maps body/heading to the extracted tokens. */ +@layer base { + html { font-family: var(--font-sans); } + body { background-color: var(--color-background); color: var(--color-foreground, currentColor); } +} diff --git a/docs/design-extract/coulomb-social-tailwind.config.js b/docs/design-extract/coulomb-social-tailwind.config.js new file mode 100644 index 0000000..f260b6a --- /dev/null +++ b/docs/design-extract/coulomb-social-tailwind.config.js @@ -0,0 +1,112 @@ +/** @type {import('tailwindcss').Config} */ +export default { + theme: { + extend: { + colors: { + primary: { + '50': 'hsl(34, 68%, 97%)', + '100': 'hsl(34, 68%, 94%)', + '200': 'hsl(34, 68%, 86%)', + '300': 'hsl(34, 68%, 76%)', + '400': 'hsl(34, 68%, 64%)', + '500': 'hsl(34, 68%, 50%)', + '600': 'hsl(34, 68%, 40%)', + '700': 'hsl(34, 68%, 32%)', + '800': 'hsl(34, 68%, 24%)', + '900': 'hsl(34, 68%, 16%)', + '950': 'hsl(34, 68%, 10%)', + DEFAULT: '#d08728' + }, + 'neutral-50': '#000000', + 'neutral-100': '#efefef', + 'neutral-200': '#616161', + 'neutral-300': '#373737', + 'neutral-400': '#171717', + 'neutral-500': '#ffffff', + 'neutral-600': '#e5e5e5', + background: '#ffffff', + foreground: '#000000' + }, + fontFamily: { + body: [ + 'Inter', + 'sans-serif' + ] + }, + fontSize: { + '14': [ + '14px', + { + lineHeight: '19.6px' + } + ], + '16': [ + '16px', + { + lineHeight: 'normal' + } + ], + '18': [ + '18px', + { + lineHeight: '18px' + } + ], + '24': [ + '24px', + { + lineHeight: '36px' + } + ], + '42': [ + '42px', + { + lineHeight: 'normal' + } + ], + '50': [ + '50px', + { + lineHeight: '50px' + } + ], + '13.3333': [ + '13.3333px', + { + lineHeight: 'normal' + } + ] + }, + spacing: { + '6': '24px', + '8': '32px', + '10': '40px', + '15': '60px', + '20': '80px', + '24': '96px', + '80': '320px', + '1px': '1px' + }, + borderRadius: { + sm: '4px', + md: '8px', + lg: '15px', + xl: '20px' + }, + boxShadow: { + sm: 'rgb(170, 170, 170) 2px 2px 4px 0px', + md: 'rgba(23, 23, 23, 0.1) 2px 2px 8px 2px' + }, + transitionDuration: { + '250': '0.25s' + }, + container: { + center: true, + padding: '0px' + }, + maxWidth: { + container: '1200px' + } +}, + }, +}; diff --git a/docs/design-extract/coulomb-social-theme.js b/docs/design-extract/coulomb-social-theme.js new file mode 100644 index 0000000..4dd24f0 --- /dev/null +++ b/docs/design-extract/coulomb-social-theme.js @@ -0,0 +1,171 @@ +// React Theme — extracted from https://coulomb.social +// Compatible with: Chakra UI, Stitches, Vanilla Extract, or any CSS-in-JS + +/** + * TypeScript type definition for this theme: + * + * interface Theme { + * colors: { + primary: string; + background: string; + foreground: string; + neutral50: string; + neutral100: string; + neutral200: string; + neutral300: string; + neutral400: string; + neutral500: string; + neutral600: string; + * }; + * fonts: { + body: string; + * }; + * fontSizes: { + '14': string; + '16': string; + '18': string; + '24': string; + '42': string; + '50': string; + '13.3333': string; + * }; + * space: { + '1': string; + '24': string; + '32': string; + '40': string; + '60': string; + '80': string; + '96': string; + '320': string; + * }; + * radii: { + sm: string; + md: string; + lg: string; + xl: string; + * }; + * shadows: { + sm: string; + md: string; + * }; + * states: { + * hover: { opacity: number }; + * focus: { opacity: number }; + * active: { opacity: number }; + * disabled: { opacity: number }; + * }; + * } + */ + +export const theme = { + "colors": { + "primary": "#d08728", + "background": "#ffffff", + "foreground": "#000000", + "neutral50": "#000000", + "neutral100": "#efefef", + "neutral200": "#616161", + "neutral300": "#373737", + "neutral400": "#171717", + "neutral500": "#ffffff", + "neutral600": "#e5e5e5" + }, + "fonts": { + "body": "'Inter', sans-serif" + }, + "fontSizes": { + "14": "14px", + "16": "16px", + "18": "18px", + "24": "24px", + "42": "42px", + "50": "50px", + "13.3333": "13.3333px" + }, + "space": { + "1": "1px", + "24": "24px", + "32": "32px", + "40": "40px", + "60": "60px", + "80": "80px", + "96": "96px", + "320": "320px" + }, + "radii": { + "sm": "4px", + "md": "8px", + "lg": "15px", + "xl": "20px" + }, + "shadows": { + "sm": "rgb(170, 170, 170) 2px 2px 4px 0px", + "md": "rgba(23, 23, 23, 0.1) 2px 2px 8px 2px" + }, + "states": { + "hover": { + "opacity": 0.08 + }, + "focus": { + "opacity": 0.12 + }, + "active": { + "opacity": 0.16 + }, + "disabled": { + "opacity": 0.38 + } + } +}; + +// MUI v5 theme +export const muiTheme = { + "palette": { + "primary": { + "main": "#d08728", + "light": "hsl(34, 68%, 64%)", + "dark": "hsl(34, 68%, 34%)" + }, + "background": { + "default": "#ffffff", + "paper": "#e5e5e5" + }, + "text": { + "primary": "#000000", + "secondary": "#171717" + } + }, + "typography": { + "fontFamily": "'Helvetica', sans-serif", + "h1": { + "fontSize": "42px", + "fontWeight": "700", + "lineHeight": "normal" + }, + "h2": { + "fontSize": "24px", + "fontWeight": "600", + "lineHeight": "36px" + }, + "body1": { + "fontSize": "16px", + "fontWeight": "400", + "lineHeight": "normal" + }, + "body2": { + "fontSize": "14px", + "fontWeight": "500", + "lineHeight": "19.6px" + } + }, + "shape": { + "borderRadius": 8 + }, + "shadows": [ + "rgb(170, 170, 170) 2px 2px 4px 0px", + "rgba(23, 23, 23, 0.1) 2px 2px 8px 2px" + ] +}; + +export default theme; diff --git a/docs/design-extract/coulomb-social-tokens-routes/index.json b/docs/design-extract/coulomb-social-tokens-routes/index.json new file mode 100644 index 0000000..1fb273b --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-routes/index.json @@ -0,0 +1,82 @@ +{ + "url": "https://coulomb.social", + "path": "/", + "added": {}, + "changed": { + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#000000" + } + }, + "neutral": { + "n200": { + "$value": "#373737" + }, + "n300": { + "$value": "#616161" + }, + "n400": { + "$value": "#171717" + }, + "n500": { + "$value": "#efefef" + }, + "n600": { + "$value": "#ffffff" + }, + "n700": { + "$value": "#e5e5e5" + } + }, + "background": { + "bg1": { + "$value": "#e5e5e5" + } + }, + "text": { + "text2": { + "$value": "#373737" + }, + "text3": { + "$value": "#616161" + } + } + }, + "spacing": { + "s1": { + "$value": "24px" + }, + "s2": { + "$value": "32px" + }, + "s3": { + "$value": "40px" + }, + "s4": { + "$value": "82px" + }, + "s5": { + "$value": "96px" + } + }, + "radius": { + "r3": { + "$value": "20px" + } + }, + "fontFamily": { + "f1": { + "$value": "Inter" + }, + "f2": { + "$value": "Arial" + }, + "f3": { + "$value": "Times New Roman" + } + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1767965374100x901547861536145400.json b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1767965374100x901547861536145400.json new file mode 100644 index 0000000..118953c --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1767965374100x901547861536145400.json @@ -0,0 +1,88 @@ +{ + "url": "https://coulomb.social/pg_chunk/1767965374100x901547861536145400", + "path": "/pg_chunk/1767965374100x901547861536145400", + "added": {}, + "changed": { + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#d08728" + } + }, + "neutral": { + "n200": { + "$value": "#efefef" + }, + "n300": { + "$value": "#e5e5e5" + }, + "n400": { + "$value": "#616161" + }, + "n500": { + "$value": "#171717" + }, + "n600": { + "$value": "#ffffff" + }, + "n700": { + "$value": "#373737" + } + }, + "background": { + "bg1": { + "$value": "#171717" + }, + "bg2": { + "$value": "#f5f5f5" + }, + "bg3": { + "$value": "#e5e5e5" + } + }, + "text": { + "text2": { + "$value": "#616161" + }, + "text3": { + "$value": "#373737" + } + } + }, + "spacing": { + "s1": { + "$value": "60px" + }, + "s2": { + "$value": "80px" + }, + "s3": { + "$value": "96px" + }, + "s4": { + "$value": "320px" + } + }, + "radius": { + "r3": { + "$value": "15px" + }, + "r4": { + "$value": "20px" + } + }, + "fontFamily": { + "f1": { + "$value": "Arial" + }, + "f2": { + "$value": "Times New Roman" + }, + "f3": { + "$value": "Inter" + } + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1784305925912x322414739506593800.json b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1784305925912x322414739506593800.json new file mode 100644 index 0000000..105e900 --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1784305925912x322414739506593800.json @@ -0,0 +1,91 @@ +{ + "url": "https://coulomb.social/pg_chunk/1784305925912x322414739506593800", + "path": "/pg_chunk/1784305925912x322414739506593800", + "added": {}, + "changed": { + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#d08728" + } + }, + "neutral": { + "n200": { + "$value": "#efefef" + }, + "n300": { + "$value": "#616161" + }, + "n400": { + "$value": "#373737" + }, + "n500": { + "$value": "#e5e5e5" + }, + "n600": { + "$value": "#171717" + }, + "n700": { + "$value": "#ffffff" + } + }, + "background": { + "bg1": { + "$value": "#171717" + }, + "bg2": { + "$value": "#f5f5f5" + }, + "bg3": { + "$value": "#e5e5e5" + } + }, + "text": { + "text2": { + "$value": "#616161" + }, + "text3": { + "$value": "#373737" + }, + "text4": { + "$value": "#ffffff" + } + } + }, + "spacing": { + "s1": { + "$value": "60px" + }, + "s2": { + "$value": "80px" + }, + "s3": { + "$value": "96px" + }, + "s4": { + "$value": "320px" + } + }, + "radius": { + "r3": { + "$value": "15px" + }, + "r4": { + "$value": "20px" + } + }, + "fontFamily": { + "f1": { + "$value": "Arial" + }, + "f2": { + "$value": "Times New Roman" + }, + "f3": { + "$value": "Inter" + } + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1784904857383x503270614975381500.json b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1784904857383x503270614975381500.json new file mode 100644 index 0000000..47eb8a0 --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1784904857383x503270614975381500.json @@ -0,0 +1,91 @@ +{ + "url": "https://coulomb.social/pg_chunk/1784904857383x503270614975381500", + "path": "/pg_chunk/1784904857383x503270614975381500", + "added": {}, + "changed": { + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#d08728" + } + }, + "neutral": { + "n200": { + "$value": "#efefef" + }, + "n300": { + "$value": "#616161" + }, + "n400": { + "$value": "#373737" + }, + "n500": { + "$value": "#e5e5e5" + }, + "n600": { + "$value": "#171717" + }, + "n700": { + "$value": "#ffffff" + } + }, + "background": { + "bg1": { + "$value": "#171717" + }, + "bg2": { + "$value": "#f5f5f5" + }, + "bg3": { + "$value": "#e5e5e5" + } + }, + "text": { + "text2": { + "$value": "#616161" + }, + "text3": { + "$value": "#373737" + }, + "text4": { + "$value": "#ffffff" + } + } + }, + "spacing": { + "s1": { + "$value": "60px" + }, + "s2": { + "$value": "80px" + }, + "s3": { + "$value": "96px" + }, + "s4": { + "$value": "320px" + } + }, + "radius": { + "r3": { + "$value": "15px" + }, + "r4": { + "$value": "20px" + } + }, + "fontFamily": { + "f1": { + "$value": "Arial" + }, + "f2": { + "$value": "Times New Roman" + }, + "f3": { + "$value": "Inter" + } + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1785501127011x363256181345746940.json b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1785501127011x363256181345746940.json new file mode 100644 index 0000000..86f4b42 --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1785501127011x363256181345746940.json @@ -0,0 +1,88 @@ +{ + "url": "https://coulomb.social/pg_chunk/1785501127011x363256181345746940", + "path": "/pg_chunk/1785501127011x363256181345746940", + "added": {}, + "changed": { + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#d08728" + } + }, + "neutral": { + "n200": { + "$value": "#efefef" + }, + "n300": { + "$value": "#e5e5e5" + }, + "n400": { + "$value": "#616161" + }, + "n500": { + "$value": "#171717" + }, + "n600": { + "$value": "#ffffff" + }, + "n700": { + "$value": "#373737" + } + }, + "background": { + "bg1": { + "$value": "#171717" + }, + "bg2": { + "$value": "#f5f5f5" + }, + "bg3": { + "$value": "#e5e5e5" + } + }, + "text": { + "text2": { + "$value": "#616161" + }, + "text3": { + "$value": "#373737" + } + } + }, + "spacing": { + "s1": { + "$value": "60px" + }, + "s2": { + "$value": "80px" + }, + "s3": { + "$value": "96px" + }, + "s4": { + "$value": "320px" + } + }, + "radius": { + "r3": { + "$value": "15px" + }, + "r4": { + "$value": "20px" + } + }, + "fontFamily": { + "f1": { + "$value": "Arial" + }, + "f2": { + "$value": "Times New Roman" + }, + "f3": { + "$value": "Inter" + } + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1785501151364x921456713843343400.json b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1785501151364x921456713843343400.json new file mode 100644 index 0000000..c38167b --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1785501151364x921456713843343400.json @@ -0,0 +1,88 @@ +{ + "url": "https://coulomb.social/pg_chunk/1785501151364x921456713843343400", + "path": "/pg_chunk/1785501151364x921456713843343400", + "added": {}, + "changed": { + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#d08728" + } + }, + "neutral": { + "n200": { + "$value": "#efefef" + }, + "n300": { + "$value": "#e5e5e5" + }, + "n400": { + "$value": "#171717" + }, + "n500": { + "$value": "#616161" + }, + "n600": { + "$value": "#ffffff" + }, + "n700": { + "$value": "#373737" + } + }, + "background": { + "bg1": { + "$value": "#171717" + }, + "bg2": { + "$value": "#f5f5f5" + }, + "bg3": { + "$value": "#e5e5e5" + } + }, + "text": { + "text2": { + "$value": "#616161" + }, + "text3": { + "$value": "#373737" + }, + "text4": { + "$value": "#ffffff" + } + } + }, + "spacing": { + "s1": { + "$value": "60px" + }, + "s2": { + "$value": "80px" + }, + "s3": { + "$value": "96px" + }, + "s4": { + "$value": "320px" + } + }, + "radius": { + "r3": { + "$value": "15px" + } + }, + "fontFamily": { + "f1": { + "$value": "Arial" + }, + "f2": { + "$value": "Times New Roman" + }, + "f3": { + "$value": "Inter" + } + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1786106348475x662489469743530000.json b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1786106348475x662489469743530000.json new file mode 100644 index 0000000..434b1ae --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-routes/pg-chunk-1786106348475x662489469743530000.json @@ -0,0 +1,99 @@ +{ + "url": "https://coulomb.social/pg_chunk/1786106348475x662489469743530000", + "path": "/pg_chunk/1786106348475x662489469743530000", + "added": { + "primitive": { + "spacing": { + "s6": { + "$value": "320px" + } + } + } + }, + "changed": { + "primitive": { + "color": { + "brand": { + "primary": { + "$value": "#d08728" + } + }, + "neutral": { + "n200": { + "$value": "#efefef" + }, + "n300": { + "$value": "#616161" + }, + "n400": { + "$value": "#171717" + }, + "n500": { + "$value": "#ffffff" + }, + "n600": { + "$value": "#373737" + }, + "n700": { + "$value": "#e5e5e5" + } + }, + "background": { + "bg1": { + "$value": "#171717" + }, + "bg2": { + "$value": "#f5f5f5" + }, + "bg3": { + "$value": "#e5e5e5" + } + }, + "text": { + "text2": { + "$value": "#616161" + }, + "text3": { + "$value": "#373737" + } + } + }, + "spacing": { + "s1": { + "$value": "24px" + }, + "s2": { + "$value": "32px" + }, + "s3": { + "$value": "40px" + }, + "s4": { + "$value": "80px" + }, + "s5": { + "$value": "96px" + } + }, + "radius": { + "r3": { + "$value": "15px" + }, + "r4": { + "$value": "20px" + } + }, + "fontFamily": { + "f1": { + "$value": "Arial" + }, + "f2": { + "$value": "Times New Roman" + }, + "f3": { + "$value": "Inter" + } + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens-shared.json b/docs/design-extract/coulomb-social-tokens-shared.json new file mode 100644 index 0000000..6cc2c46 --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens-shared.json @@ -0,0 +1,92 @@ +{ + "primitive": { + "color": { + "neutral": { + "n100": { + "$value": "#000000" + } + }, + "background": { + "bg0": { + "$value": "#ffffff" + } + }, + "text": { + "text0": { + "$value": "#000000" + }, + "text1": { + "$value": "#171717" + } + } + }, + "spacing": { + "s0": { + "$value": "1px" + } + }, + "radius": { + "r0": { + "$value": "4px" + }, + "r1": { + "$value": "8px" + }, + "r2": { + "$value": "12px" + } + }, + "shadow": { + "sh0": { + "$value": "rgb(170, 170, 170) 2px 2px 4px 0px" + }, + "sh1": { + "$value": "rgba(23, 23, 23, 0.1) 2px 2px 8px 2px" + } + }, + "fontFamily": { + "f0": { + "$value": "Helvetica" + } + } + }, + "semantic": { + "color": { + "action": { + "primary": { + "$value": "{primitive.color.brand.primary}" + } + }, + "surface": { + "default": { + "$value": "{primitive.color.background.bg0}" + } + }, + "text": { + "body": { + "$value": "{primitive.color.text.text0}" + } + } + }, + "typography": { + "body": { + "$value": { + "fontFamily": "Helvetica", + "fontSize": "50px", + "fontWeight": "400", + "lineHeight": "50px" + } + } + }, + "radius": { + "control": { + "$value": "{primitive.radius.r0}" + } + }, + "shadow": { + "elevated": { + "$value": "{primitive.shadow.sh0}" + } + } + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-tokens.d.ts b/docs/design-extract/coulomb-social-tokens.d.ts new file mode 100644 index 0000000..21a6484 --- /dev/null +++ b/docs/design-extract/coulomb-social-tokens.d.ts @@ -0,0 +1,49 @@ +// Auto-generated by designlang. Do not edit by hand. +// Re-extract with `npx designlang ` to refresh. +// +// Import next to your token JSON to get type-safe access: +// +// import type { ColorToken, SpacingToken } from './-tokens'; +// import tokens from './-design-tokens.json'; +// +// Source: https://coulomb.social +// Generated: 2026-08-08T23:30:22.004Z + +export type Hex = `#${string}`; +export type Px = `${number}px`; +export type Ms = `${number}ms`; + +export type ColorRole = 'primary'; +export type ColorHex = '#000000' | '#efefef' | '#616161' | '#373737' | '#171717' | '#ffffff' | '#e5e5e5' | '#d08728'; +export type ColorToken = ColorRole | ColorHex; + +export type FontFamilyToken = 'Helvetica' | 'Arial' | 'Times New Roman' | 'Inter'; +export type FontSizeToken = never; +export type FontWeightToken = '400' | '500' | '600' | '700'; + +export type SpacingToken = '1px' | '24px' | '32px' | '40px' | '60px' | '80px' | '96px' | '320px'; + +export type RadiusToken = '4px' | '8px' | '12px' | '15px' | '20px'; + +export type DurationToken = '250ms'; +export type EasingToken = never; + +export type MaterialLanguage = 'flat'; + +export interface DesignTokens { + color: { [role in ColorRole]: ColorHex }; + typography: { + family: FontFamilyToken; + size: FontSizeToken; + weight: FontWeightToken; + }; + spacing: ReadonlyArray; + radius: ReadonlyArray; + motion: { + duration: ReadonlyArray; + easing: ReadonlyArray; + }; +} + +declare const tokens: DesignTokens; +export default tokens; diff --git a/docs/design-extract/coulomb-social-variables.css b/docs/design-extract/coulomb-social-variables.css new file mode 100644 index 0000000..784e844 --- /dev/null +++ b/docs/design-extract/coulomb-social-variables.css @@ -0,0 +1,106 @@ +:root { + /* Colors — Primary */ + --color-primary: #d08728; + + /* Colors — Neutrals */ + --color-neutral-50: #000000; + --color-neutral-100: #efefef; + --color-neutral-200: #616161; + --color-neutral-300: #373737; + --color-neutral-400: #171717; + --color-neutral-500: #ffffff; + --color-neutral-600: #e5e5e5; + + /* Colors — Backgrounds */ + --color-bg: #ffffff; + --color-bg-1: #e5e5e5; + --color-bg-2: #171717; + --color-bg-3: #f5f5f5; + + /* Colors — Text */ + --color-text: #000000; + --color-text-1: #171717; + --color-text-2: #373737; + --color-text-3: #616161; + --color-text-4: #ffffff; + + /* Typography — Families */ + --font-body: 'Helvetica', sans-serif; + --font-body: 'Arial', sans-serif; + --font-body: 'Times New Roman', sans-serif; + --font-body: 'Inter', sans-serif; + + /* Typography — Scale */ + --font-size-50: 50px; + --font-size-42: 42px; + --font-size-24: 24px; + --font-size-18: 18px; + --font-size-16: 16px; + --font-size-14: 14px; + --font-size-13.3333: 13.3333px; + + /* Spacing */ + --spacing-1: 1px; + --spacing-24: 24px; + --spacing-32: 32px; + --spacing-40: 40px; + --spacing-60: 60px; + --spacing-80: 80px; + --spacing-96: 96px; + --spacing-320: 320px; + + /* Border Radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-lg: 15px; + --radius-xl: 20px; + + /* Box Shadows */ + --shadow-sm: rgb(170, 170, 170) 2px 2px 4px 0px; + --shadow-md: rgba(23, 23, 23, 0.1) 2px 2px 8px 2px; + + /* Original Site Variables */ + /* — colors — */ + --color_primary_default: rgba(2, 5, 211, 1); + --color_primary_default_rgb: 2, 5, 211; + --color_primary_contrast_default: rgba(255, 255, 255, 1); + --color_primary_contrast_default_rgb: 255, 255, 255; + --color_text_default: rgba(9, 23, 71, 1); + --color_text_default_rgb: 9, 23, 71; + --color_surface_default: rgba(255, 255, 255, 1); + --color_surface_default_rgb: 255, 255, 255; + --color_background_default: rgba(255, 255, 255, 0); + --color_background_default_rgb: 255, 255, 255; + --color_destructive_default: rgba(255, 0, 0, 1); + --color_destructive_default_rgb: 255, 0, 0; + --color_success_default: rgba(23, 219, 78, 1); + --color_success_default_rgb: 23, 219, 78; + --color_alert_default: rgba(250, 181, 21, 1); + --color_alert_default_rgb: 250, 181, 21; + --color_danger_default_rgb: rgba(176, 32, 12, 1); + --color_bTRUr_default_rgb: 208,135,40; + --color_bTOCs_default: rgba(55,55,55,1); + --color_bTHZK_default_rgb: 229,229,229; + --color_bTOCs_default_rgb: 55,55,55; + --color_bTINZ_default_rgb: 23,23,23; + --color_bTRUs_default_rgb: 227,230,235; + --color_bTINZ_default: rgba(23,23,23,0.36); + --color_bTRUr_default: rgba(208,135,40,0.1); + --color_bTHZK_default: rgba(229,229,229,1); + --color_bTHfJ_default_rgb: 97,97,97; + --color_bTMap_default: rgba(113,2,139,1); + --color_bTHfJ_default: rgba(97,97,97,1); + --color_bTMap_default_rgb: 113,2,139; + --color_bTRUs_default: rgba(227,230,235,1); + /* — typography — */ + --font_default: 'Lato'; + --font_bTRUx_default: "Inter"; + --font_bTRUt_default: "Inter"; + /* — semantic — */ + success: [object Object]; + warning: [object Object]; + error: [object Object]; + info: [object Object]; + +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-visual-dna.json b/docs/design-extract/coulomb-social-visual-dna.json new file mode 100644 index 0000000..b75343b --- /dev/null +++ b/docs/design-extract/coulomb-social-visual-dna.json @@ -0,0 +1,53 @@ +{ + "materialLanguage": { + "label": "flat", + "confidence": 0, + "signals": [], + "metrics": { + "saturation": 0.101, + "shadowProfile": "soft", + "avgShadowBlur": 0, + "maxShadowBlur": 0, + "insetShadows": 0, + "avgRadius": 11.8, + "maxRadius": 20, + "hasPill": false, + "hasBackdropBlur": false, + "gradientCount": 0 + }, + "alternates": [] + }, + "imageryStyle": { + "label": "mixed", + "confidence": 0, + "counts": { + "total": 29, + "svg": 0, + "icon": 1, + "hero": 0, + "screenshot": 0, + "photoLike": 0 + }, + "dominantAspect": "square-ish", + "radiusProfile": "soft", + "alternates": [], + "signals": [] + }, + "backgroundPatterns": { + "labels": [ + "plain" + ], + "counts": { + "noise": 0, + "dotGrid": 0, + "lineGrid": 0, + "meshCount": 0, + "svgPattern": 0 + }, + "gradientTotals": { + "radial": 0, + "linear": 0 + }, + "samples": [] + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-voice.json b/docs/design-extract/coulomb-social-voice.json new file mode 100644 index 0000000..ecf977e --- /dev/null +++ b/docs/design-extract/coulomb-social-voice.json @@ -0,0 +1,71 @@ +{ + "tone": "neutral", + "pronoun": "third-person", + "headingStyle": "unknown", + "headingLengthClass": "tight", + "ctaVerbs": [ + { + "value": "content", + "count": 25 + }, + { + "value": "favorite", + "count": 25 + }, + { + "value": "format", + "count": 1 + }, + { + "value": "filter", + "count": 1 + }, + { + "value": "add", + "count": 1 + }, + { + "value": "arrow", + "count": 1 + }, + { + "value": "note", + "count": 1 + } + ], + "buttonPatterns": [ + { + "value": "content_copy", + "count": 25 + }, + { + "value": "favorite_border", + "count": 25 + }, + { + "value": "format_line_spacing", + "count": 1 + }, + { + "value": "filter_list", + "count": 1 + }, + { + "value": "add_circle_outline", + "count": 1 + }, + { + "value": "arrow_drop_down", + "count": 1 + }, + { + "value": "note_alt", + "count": 1 + } + ], + "sampleHeadings": [], + "stats": { + "buttons": 55, + "headings": 0 + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social-wordpress-theme.json b/docs/design-extract/coulomb-social-wordpress-theme.json new file mode 100644 index 0000000..3819fca --- /dev/null +++ b/docs/design-extract/coulomb-social-wordpress-theme.json @@ -0,0 +1,175 @@ +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 3, + "settings": { + "color": { + "palette": [ + { + "slug": "primary", + "color": "#d08728", + "name": "Primary" + }, + { + "slug": "neutral-1", + "color": "#000000", + "name": "Neutral 1" + }, + { + "slug": "neutral-2", + "color": "#efefef", + "name": "Neutral 2" + }, + { + "slug": "neutral-3", + "color": "#616161", + "name": "Neutral 3" + }, + { + "slug": "neutral-4", + "color": "#373737", + "name": "Neutral 4" + }, + { + "slug": "neutral-5", + "color": "#171717", + "name": "Neutral 5" + }, + { + "slug": "bg-ffffff", + "color": "#ffffff", + "name": "Background #ffffff" + }, + { + "slug": "bg-e5e5e5", + "color": "#e5e5e5", + "name": "Background #e5e5e5" + }, + { + "slug": "bg-171717", + "color": "#171717", + "name": "Background #171717" + } + ], + "gradients": [] + }, + "typography": { + "fontFamilies": [ + { + "fontFamily": "Helvetica", + "slug": "helvetica", + "name": "Helvetica" + }, + { + "fontFamily": "Arial", + "slug": "arial", + "name": "Arial" + }, + { + "fontFamily": "Times New Roman", + "slug": "times-new-roman", + "name": "Times New Roman" + }, + { + "fontFamily": "Inter", + "slug": "inter", + "name": "Inter" + } + ], + "fontSizes": [ + { + "size": "50px", + "slug": "size-50", + "name": "50px" + }, + { + "size": "42px", + "slug": "size-42", + "name": "42px" + }, + { + "size": "24px", + "slug": "size-24", + "name": "24px" + }, + { + "size": "18px", + "slug": "size-18", + "name": "18px" + }, + { + "size": "16px", + "slug": "size-16", + "name": "16px" + }, + { + "size": "14px", + "slug": "size-14", + "name": "14px" + }, + { + "size": "13.3333px", + "slug": "size-13.3333", + "name": "13.3333px" + } + ] + }, + "spacing": { + "spacingSizes": [ + { + "size": "1px", + "slug": "spacing-1", + "name": "1px" + }, + { + "size": "24px", + "slug": "spacing-24", + "name": "24px" + }, + { + "size": "32px", + "slug": "spacing-32", + "name": "32px" + }, + { + "size": "40px", + "slug": "spacing-40", + "name": "40px" + }, + { + "size": "60px", + "slug": "spacing-60", + "name": "60px" + }, + { + "size": "80px", + "slug": "spacing-80", + "name": "80px" + }, + { + "size": "96px", + "slug": "spacing-96", + "name": "96px" + }, + { + "size": "320px", + "slug": "spacing-320", + "name": "320px" + } + ] + }, + "layout": { + "contentSize": "1200px", + "wideSize": "1400px" + } + }, + "styles": { + "color": { + "background": "#ffffff", + "text": "#000000" + }, + "typography": { + "fontFamily": "Helvetica" + }, + "spacing": {} + } +} \ No newline at end of file diff --git a/docs/design-extract/coulomb-social.brand.html b/docs/design-extract/coulomb-social.brand.html new file mode 100644 index 0000000..0c8ad76 --- /dev/null +++ b/docs/design-extract/coulomb-social.brand.html @@ -0,0 +1,795 @@ + + + + + +coulomb.social · brand guidelines + + + + + + + + + + + +
+ + +
+ +
+ +
+
+ Brand guidelines + #d08728 +
+
+

August 9, 2026

+

coulomb.social

+

A reading of the visual language at https://coulomb.social. Every token, every rule, every component — captured from the live site.

+
+
Page intent
landing
+
System grade
B 85/100
+
Tokens
8 colours · 7 sizes
+
Generated
designlang
+
+
+
+ + + +
+ +
+ Chapter 01 +

About

+
+
+
Page intent
landing 45% confidence
+
Material language
flat
+
Imagery style
mixed
+
Component library
unknown
+
Stack
unknown · Tailwind
+
Voice tone
neutral
+
+
+ + + +
+ +
+ Chapter 03 +

Colour

+
+

1 primary · 7 neutrals · 8 total

+ +
+
+
+ Primary + #D08728 +
+
+
RGB
208, 135, 40
+
HSL
34°, 68%, 49%
+
+
+ + + + +

Neutrals

+
+
#000000
#EFEFEF
#616161
#373737
#171717
#FFFFFF
#E5E5E5
+
+ + +

Full palette

+
+
#000000
#EFEFEF
#616161
#373737
#171717
#FFFFFF
#E5E5E5
#D08728
+
+ + +

WCAG 100% · 0 passing pairs · 0 failing. Full breakdown in §11.

+ +
+ +
+ +
+ Chapter 04 +

Typography

+
+

4 families · 7 sizes · 4 weights

+ +
+
Display
Helvetica
+
Body
Arial
+
Weights
400, 500, 600, 700
+
+ +

Specimen

+
+
Quick brown fox jumps over the lazy dog.
+
AaBbCc 0123456789 ?!&
+
+ +

Scale

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StepSizeSample
t0050pxQuick brown fox jumps over t
t0142pxQuick brown fox jumps over t
t0224pxQuick brown fox jumps over t
t0318pxQuick brown fox jumps over t
t0416pxQuick brown fox jumps over t
t0514pxQuick brown fox jumps over t
t0613.3333pxQuick brown fox jumps over t
+
+ +
+ +
+ Chapter 05 +

Spacing

+
+

Base 4px · 8 steps captured

+ +

Rhythm

+
+ +
+
+ 1px +
+ +
+
+ 24px +
+ +
+
+ 32px +
+ +
+
+ 40px +
+ +
+
+ 60px +
+ +
+
+ 80px +
+ +
+
+ 96px +
+ +
+
+ 320px +
+ +
+
+ +
+ +
+ Chapter 06 +

Shape

+
+

5 radii · 2 elevation tiers

+ +

Border radii

+
+ +
+
+ 4px +
+ +
+
+ 8px +
+ +
+
+ 12px +
+ +
+
+ 15px +
+ +
+
+ 20px +
+ +
+ + +

Elevation

+
+ +
+
+ xs +
+ +
+
+ sm +
+ +
+ +
+ +
+ +
+ Chapter 07 +

Iconography

+
+

unknown · 24 captured

+ +
+
Library
unknown
+
Confidence
0%
+
Stroke style
fillDominant
+
+ + +
+
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
icon
+
+ +
+ +
+ +
+ Chapter 08 +

Motion

+
+

Feel: mixed · 1 durations · 0 easings

+ + +

Duration scale

+
+ +
+
+ 250ms +
+ +
+ + + +
+ +
+ +
+ Chapter 09 +

Components

+
+

unknown · 1 component patterns captured

+ +

Mocks

+
+
+ + +
+
+ Card +

Built from these tokens

+

Radius, primary, surface, text — all sampled from the live site.

+ Read more → +
+
+ + +

Detected patterns

+
+ +
+

button

+
+
Slots
label, icon, badge
+
Variants
—
+
Sizes
—
+
+
+ +
+ +
+ +
+ +
+ Chapter 10 +

Voice

+
+

neutral · — · —

+ + + + +

CTA verbs

+
    +
  • ×25
  • ×25
  • ×1
  • ×1
  • ×1
  • ×1
  • ×1
  • +
+ +
+ +
+ +
+ Chapter 11 +

Accessibility

+
+ +
+ 100% + WCAG 2.1 contrast
0 passing · 0 failing
+
+ +

No failing contrast pairs detected.

+ + +
+ +
+ +
+ Chapter 12 +

Tokens

+
+

Drop-in code for the most common stacks. All values from the live extraction.

+ +
+
CSS variablesvariables.css
+
:root {
+  --color-primary: #d08728;
+  --font-display: "Helvetica";
+  --space-base: 4px;
+  --radius-md: 8px;
+}
+
+ +
+
Tailwind configtailwind.config.js
+
// tailwind.config.js
+module.exports = {
+  theme: {
+    extend: {
+      colors: { brand: '#d08728' },
+      fontFamily: { display: ['Helvetica', 'serif'] },
+      spacing: { base: '4px' },
+      borderRadius: { md: '8px' },
+    },
+  },
+};
+
+ +

Run npx designlang pack coulomb.social for the full bundle (DTCG, shadcn, Figma vars, motion, anatomy, Storybook).

+
+ +
+ +
+ Chapter 13 +

Usage

+
+
    + +
  1. +

    Lead with the primary.

    +

    It belongs on calls-to-action and one accent moment per screen. Not on body copy.

    +
  2. + +
  3. +

    Two type families, three weights.

    +

    Display for headlines, body for paragraphs. Resist a third unless there is a real reason.

    +
  4. + +
  5. +

    Snap to the spacing scale.

    +

    Padding, margin, and gap should land on the values in §05. One-off pixels accumulate into noise.

    +
  6. + +
  7. +

    Treat accessibility as a hard constraint.

    +

    When a colour pair fails WCAG, fix the colour — not the contrast check.

    +
  8. + +
+
+ +
+
+
designlang
+
Re-run: npx designlang brand coulomb.social
+
+
Aug 9, 2026 · v12.21.0
+
+
+ + + + \ No newline at end of file diff --git a/scripts/capture-auth-state.mjs b/scripts/capture-auth-state.mjs new file mode 100755 index 0000000..7e1e0ea --- /dev/null +++ b/scripts/capture-auth-state.mjs @@ -0,0 +1,194 @@ +#!/usr/bin/env node +/** + * Capture a Playwright storageState for https://coulomb.social after you log in. + * + * Default: headed browser — you complete login, then press Enter in this terminal. + * Optional: --from-password-files reads EMAIL/PASSWORD from XDG auth dir (OpenBao-exported). + * + * Never prints secret values. Writes mode-0600 storage-state.json only. + */ +import { chromium } from "playwright"; +import { + mkdirSync, + writeFileSync, + chmodSync, + existsSync, + readFileSync, + statSync, +} from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; +import { createInterface } from "node:readline/promises"; +import { stdin as input, stdout as output } from "node:process"; + +const BASE_URL = process.env.COULOMB_SOCIAL_URL || "https://coulomb.social"; +const AUTH_DIR = + process.env.COULOMB_SOCIAL_AUTH_DIR || + join(homedir(), ".config", "coulomb-social", "auth"); +const STATE_PATH = join(AUTH_DIR, "storage-state.json"); +const EMAIL_PATH = join(AUTH_DIR, "email"); +const PASSWORD_PATH = join(AUTH_DIR, "password"); + +const fromPasswordFiles = process.argv.includes("--from-password-files"); +const headless = process.argv.includes("--headless"); + +function ensureAuthDir() { + mkdirSync(AUTH_DIR, { recursive: true, mode: 0o700 }); + try { + chmodSync(AUTH_DIR, 0o700); + } catch { + /* best effort */ + } +} + +function readSecretFile(path) { + if (!existsSync(path)) { + throw new Error(`Missing secret file: ${path}`); + } + const mode = statSync(path).mode & 0o777; + if (mode & 0o077) { + console.warn( + `warning: ${path} is group/world-readable (mode ${mode.toString(8)}); chmod 600 recommended`, + ); + } + return readFileSync(path, "utf8").trim(); +} + +async function waitForUserContinue() { + const rl = createInterface({ input, output }); + console.log(""); + console.log(">>> Log in as your coulomb.social user in the browser window."); + console.log(">>> Navigate to an authenticated page you want designlang to see."); + console.log(">>> Then return here and press Enter to save the session."); + console.log(""); + await rl.question("Press Enter when logged in… "); + rl.close(); +} + +async function tryPasswordLogin(page, email, password) { + // Best-effort Bubble-style login. Interactive capture is preferred when MFA/CAPTCHA appears. + await page.goto(BASE_URL, { waitUntil: "domcontentloaded", timeout: 60_000 }); + await page.waitForTimeout(1500); + + const emailBox = + (await page.$('input[type="email"]')) || + (await page.$('input[name*="email" i]')) || + (await page.$('input[placeholder*="email" i]')) || + (await page.$('input[placeholder*="Email" i]')); + const passBox = + (await page.$('input[type="password"]')) || + (await page.$('input[name*="password" i]')); + + if (!emailBox || !passBox) { + throw new Error( + "Could not find email/password fields automatically. Re-run without --from-password-files and log in interactively.", + ); + } + + await emailBox.fill(email); + await passBox.fill(password); + + const submit = + (await page.$('button[type="submit"]')) || + (await page.$('input[type="submit"]')) || + (await page.$('button:has-text("Log in")')) || + (await page.$('button:has-text("Login")')) || + (await page.$('button:has-text("Sign in")')); + + if (submit) { + await submit.click(); + } else { + await passBox.press("Enter"); + } + + await page.waitForTimeout(4000); + console.log("Password login attempt finished (no secrets printed)."); + console.log("If MFA/CAPTCHA is required, re-run in interactive mode."); +} + +async function main() { + ensureAuthDir(); + + console.log(`Auth dir: ${AUTH_DIR}`); + console.log(`Target: ${BASE_URL}`); + console.log(`State → ${STATE_PATH}`); + + const browser = await chromium.launch({ + headless: Boolean(headless && fromPasswordFiles), + channel: process.env.COULOMB_SOCIAL_CHROME_CHANNEL || undefined, + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 900 }, + }); + const page = await context.newPage(); + + try { + if (fromPasswordFiles) { + const email = readSecretFile(EMAIL_PATH); + const password = readSecretFile(PASSWORD_PATH); + await tryPasswordLogin(page, email, password); + } else { + await page.goto(BASE_URL, { + waitUntil: "domcontentloaded", + timeout: 60_000, + }); + await waitForUserContinue(); + } + + const state = await context.storageState(); + const cookieCount = state.cookies?.length ?? 0; + const originCount = state.origins?.length ?? 0; + const lsKeys = (state.origins || []).flatMap((o) => + (o.localStorage || []).map((e) => e.name), + ); + + // Bubble often keeps the same coulomb_* cookie *names* for guests and + // members — names alone are not a login signal. Probe the live page: + // authenticated app usually leaves the bare landing path. + let probeUrl = page.url(); + let probeTitle = await page.title().catch(() => ""); + const path = (() => { + try { + return new URL(probeUrl).pathname; + } catch { + return ""; + } + })(); + const looksLikeLanding = path === "/" || path === ""; + + if (looksLikeLanding && !fromPasswordFiles) { + console.error(""); + console.error( + "WARNING: Still on the site root after capture — login may not have completed.", + ); + console.error(` url: ${probeUrl}`); + console.error(` title: ${probeTitle}`); + console.error( + "Log in fully, open an app page (e.g. Research / Pages), then press Enter.", + ); + // Continue to save anyway — Bubble guest+member share cookie names; user + // may still have a valid session that only reveals itself after navigation. + } + + writeFileSync(STATE_PATH, JSON.stringify(state, null, 2) + "\n", { + mode: 0o600, + }); + chmodSync(STATE_PATH, 0o600); + + console.log(""); + console.log( + `Saved storageState (${cookieCount} cookies, ${originCount} origins, localStorage keys: ${lsKeys.length}).`, + ); + console.log(` page at save: ${probeUrl} — ${probeTitle}`); + console.log(`File: ${STATE_PATH}`); + console.log("Next: ./scripts/run-design-extract.sh"); + } finally { + await browser.close(); + } +} + +main().catch((err) => { + console.error(err.message || err); + process.exit(1); +}); diff --git a/scripts/capture-auth-state.sh b/scripts/capture-auth-state.sh new file mode 100755 index 0000000..4bcaaef --- /dev/null +++ b/scripts/capture-auth-state.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Wrapper so Node resolves playwright from scripts/node_modules. +set -euo pipefail +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$DIR" +if [[ ! -d node_modules/playwright ]]; then + echo "Installing playwright in scripts/ …" + npm install + npx playwright install chromium +fi +exec node ./capture-auth-state.mjs "$@" diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..ecf078f --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,9 @@ +{ + "name": "coulomb-social-scripts", + "private": true, + "type": "module", + "description": "Local tooling for auth capture and design extract (no app runtime)", + "dependencies": { + "playwright": "^1.49.0" + } +} diff --git a/scripts/probe-auth-session.mjs b/scripts/probe-auth-session.mjs new file mode 100755 index 0000000..075a1bf --- /dev/null +++ b/scripts/probe-auth-session.mjs @@ -0,0 +1,21 @@ +#!/usr/bin/env node +/** Print final URL after loading coulomb.social with storageState (no secrets). */ +import { chromium } from "playwright"; +import { homedir } from "os"; +import { join } from "path"; + +const statePath = + process.env.COULOMB_SOCIAL_STORAGE_STATE || + join(homedir(), ".config/coulomb-social/auth/storage-state.json"); +const url = process.env.COULOMB_SOCIAL_URL || "https://coulomb.social"; + +const browser = await chromium.launch({ headless: true }); +try { + const context = await browser.newContext({ storageState: statePath }); + const page = await context.newPage(); + await page.goto(url, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(3000); + console.log(page.url()); +} finally { + await browser.close(); +} diff --git a/scripts/run-design-extract.sh b/scripts/run-design-extract.sh new file mode 100755 index 0000000..a2d8f90 --- /dev/null +++ b/scripts/run-design-extract.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Run designlang against coulomb.social using a captured Playwright storageState. +# Does not print secrets. Requires: ./scripts/capture-auth-state.sh first. +# +# NOTE: designlang's `pack` subcommand does NOT pass --cookie-file into the +# crawler (it calls extractDesignLanguage(url) with no auth options). Use the +# main extract command, which does apply cookies from storageState. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +AUTH_DIR="${COULOMB_SOCIAL_AUTH_DIR:-$HOME/.config/coulomb-social/auth}" +STATE_PATH="${COULOMB_SOCIAL_STORAGE_STATE:-$AUTH_DIR/storage-state.json}" +OUT_DIR="${COULOMB_SOCIAL_EXTRACT_OUT:-$ROOT/docs/design-extract}" +URL="${COULOMB_SOCIAL_URL:-https://coulomb.social}" +WAIT_MS="${COULOMB_SOCIAL_EXTRACT_WAIT:-5000}" + +if [[ ! -f "$STATE_PATH" ]]; then + echo "ERROR: No auth session at $STATE_PATH" >&2 + echo "Capture one first (interactive login as your user):" >&2 + echo " $ROOT/scripts/capture-auth-state.sh" >&2 + echo "Docs: $ROOT/docs/design-extract-auth.md" >&2 + exit 1 +fi + +mode="$(stat -c '%a' "$STATE_PATH" 2>/dev/null || stat -f '%Lp' "$STATE_PATH" 2>/dev/null || echo '?')" +if [[ "$mode" != "600" && "$mode" != "0600" ]]; then + echo "warning: $STATE_PATH mode is $mode (prefer 600)" >&2 +fi + +echo "==> probing session (no secrets printed)…" +PROBE_URL="$( + cd "$ROOT/scripts" && \ + COULOMB_SOCIAL_STORAGE_STATE="$STATE_PATH" \ + COULOMB_SOCIAL_URL="$URL" \ + node ./probe-auth-session.mjs +)" || PROBE_URL="" +if [[ -n "$PROBE_URL" ]]; then + echo " session lands on: $PROBE_URL" + if [[ "$PROBE_URL" == "https://coulomb.social/" || "$PROBE_URL" == "https://coulomb.social" ]]; then + echo "warning: session still on site root — may be guest; re-run capture-auth-state.sh if extract looks empty" >&2 + fi +fi + +mkdir -p "$OUT_DIR" + +echo "==> designlang extract (main command — cookies applied)" +echo " url: $URL" +echo " auth: $STATE_PATH (session file present; not printed)" +echo " out: $OUT_DIR" +echo " wait: ${WAIT_MS}ms" +echo " extra: $*" + +# Main command supports --cookie-file. Prefer a rich capture of the live app UI. +set +e +npx --yes designlang "$URL" \ + --cookie-file "$STATE_PATH" \ + -o "$OUT_DIR" \ + -n coulomb-social \ + --wait "$WAIT_MS" \ + --depth "${COULOMB_SOCIAL_EXTRACT_DEPTH:-2}" \ + --interactions \ + --verbose \ + "$@" +dl_ec=$? +set -e + +# designlang can crash late while printing summary (Buffer.byteLength on undefined +# content for optional emitters) after already writing the useful artifacts. +if [[ $dl_ec -ne 0 ]]; then + if [[ -f "$OUT_DIR/coulomb-social-design-language.md" && -f "$OUT_DIR/coulomb-social-design-tokens.json" ]]; then + echo "warning: designlang exited $dl_ec after writing core artifacts (known late summary bug); treating as success" >&2 + else + echo "ERROR: designlang failed (exit $dl_ec) and core artifacts are missing" >&2 + exit "$dl_ec" + fi +fi + +# Drop root-level pack leftovers from earlier mistaken `pack` runs (no cookies). +if [[ -d "$ROOT/coulomb-social-design-system" ]]; then + echo "==> removing unauthenticated pack leftover: coulomb-social-design-system/" + rm -rf "$ROOT/coulomb-social-design-system" +fi + +# Provenance (no secrets) +cat > "$OUT_DIR/PROVENANCE.md" < done. Inspect: $OUT_DIR" +ls -la "$OUT_DIR" | head -50