{"id":44622856,"url":"https://github.com/uniweb/theming","last_synced_at":"2026-02-19T19:00:40.928Z","repository":{"id":338167885,"uuid":"1156837297","full_name":"uniweb/theming","owner":"uniweb","description":"Theming utilities for website building.","archived":false,"fork":false,"pushed_at":"2026-02-13T13:05:10.000Z","size":33,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-14T21:37:17.188Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/uniweb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-13T05:27:27.000Z","updated_at":"2026-02-13T13:05:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/uniweb/theming","commit_stats":null,"previous_names":["uniweb/theming"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/uniweb/theming","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uniweb%2Ftheming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uniweb%2Ftheming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uniweb%2Ftheming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uniweb%2Ftheming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/uniweb","download_url":"https://codeload.github.com/uniweb/theming/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uniweb%2Ftheming/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29481924,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T11:35:25.641Z","status":"ssl_error","status_checked_at":"2026-02-15T11:34:57.128Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-14T14:34:51.157Z","updated_at":"2026-02-16T16:01:40.559Z","avatar_url":"https://github.com/uniweb.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @uniweb/theming\n\nTheming engine for [Uniweb](https://github.com/uniweb) — generates color palettes, semantic CSS tokens, and context classes from a declarative `theme.yml` configuration.\n\n## Overview\n\nUniweb sites separate theming from components. Content authors set `theme: light|medium|dark` per section in frontmatter; the runtime applies context classes; components use semantic CSS tokens (`var(--heading)`, `var(--link)`, `var(--section)`) that resolve automatically. This package is the engine behind that system.\n\nIt handles three concerns:\n\n1. **Shade generation** — Expand a single hex color into 11 perceptually uniform shades (50–950) using the OKLCH color space\n2. **Theme processing** — Validate and merge `theme.yml` configuration with foundation defaults\n3. **CSS generation** — Produce complete CSS with palette variables, context classes, font imports, and foundation-specific custom properties\n\n## Installation\n\n```bash\nnpm install @uniweb/theming\n```\n\n## Quick Start\n\n```javascript\nimport { buildTheme } from '@uniweb/theming'\n\n// Process theme.yml and generate CSS in one step\nconst { css, config, errors, warnings } = buildTheme({\n  colors: {\n    primary: '#3b82f6',\n    neutral: 'stone',      // Named preset (warm gray)\n  },\n  fonts: {\n    heading: '\"Inter\", sans-serif',\n  },\n})\n\n// css → complete stylesheet with palettes, contexts, fonts\n// config → processed configuration for runtime use\n```\n\n## Shade Generation\n\nGenerate Tailwind-compatible shade scales from any CSS color. Uses OKLCH for perceptually uniform lightness steps with automatic sRGB gamut mapping.\n\n```javascript\nimport { generateShades } from '@uniweb/theming'\n\n// Default: shade 500 = exact input color, others redistributed proportionally\nconst shades = generateShades('#3b82f6')\n// { 50: 'oklch(...)', 100: '...', ..., 500: 'oklch(...)', ..., 950: '...' }\n\n// Hex output\ngenerateShades('#3b82f6', { format: 'hex' })\n// { 50: '#eff6ff', ..., 500: '#3b82f6', ..., 950: '#172554' }\n\n// Fixed lightness scale (shade 500 may differ from input)\ngenerateShades('#3b82f6', { exactMatch: false })\n\n// Generation modes\ngenerateShades('#e35d25', { mode: 'natural' })  // Temperature-aware hue shifts\ngenerateShades('#e35d25', { mode: 'vivid' })    // Higher saturation\ngenerateShades('#e35d25', { mode: 'fixed' })    // Default — constant hue\n```\n\n### Multiple Palettes\n\n```javascript\nimport { generatePalettes } from '@uniweb/theming'\n\nconst palettes = generatePalettes({\n  primary: '#3b82f6',\n  secondary: '#64748b',\n  accent: { base: '#8b5cf6', mode: 'vivid' },  // Per-color options\n})\n// { primary: { 50: ..., 950: ... }, secondary: { ... }, accent: { ... } }\n```\n\n### Color Parsing\n\nAccepts hex, RGB, HSL, and OKLCH formats:\n\n```javascript\nimport { parseColor, isValidColor } from '@uniweb/theming'\n\nparseColor('#3b82f6')                 // { l: 0.623, c: 0.214, h: 259.8 }\nparseColor('rgb(59, 130, 246)')       // Same result\nparseColor('hsl(217, 91%, 60%)')      // Same result\nparseColor('oklch(62.3% 0.214 259.8)') // Same result\n\nisValidColor('#3b82f6')  // true\nisValidColor('not-a-color')  // false\n```\n\n### Utility Exports\n\n```javascript\nimport { formatOklch, formatHex, getShadeLevels } from '@uniweb/theming'\n\nformatOklch(0.55, 0.2, 250)   // 'oklch(55.0% 0.2000 250.0)'\nformatHex(59, 130, 246)       // '#3b82f6'\ngetShadeLevels()              // [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]\n```\n\n## Theme Processing\n\nValidate and process raw `theme.yml` into a complete configuration, merging with defaults and resolving named presets.\n\n```javascript\nimport { processTheme } from '@uniweb/theming'\n\nconst { config, errors, warnings } = processTheme({\n  colors: {\n    primary: '#e35d25',\n    neutral: 'stone',        // Resolves to #78716c\n  },\n  contexts: {\n    dark: {\n      primary: 'primary-400',  // Bare palette ref → var(--primary-400)\n      link: '#60a5fa',          // Hex passes through\n    },\n  },\n  fonts: {\n    body: '\"Inter\", sans-serif',\n    import: [{ url: 'https://fonts.googleapis.com/css2?family=Inter' }],\n  },\n  appearance: 'light',          // or { default: 'dark', allowToggle: true }\n  vars: {\n    'header-height': '5rem',    // Override foundation variable\n  },\n}, {\n  foundationVars: {             // Declared by foundation\n    'header-height': { default: '4rem' },\n    'sidebar-width': { default: '280px' },\n  },\n})\n```\n\n### Named Neutral Presets\n\nThe `neutral` color accepts preset names that map to Tailwind gray families:\n\n| Preset | Hex | Character |\n|---|---|---|\n| `stone` | `#78716c` | Warm (default) |\n| `zinc` | `#71717a` | Cool blue-gray |\n| `gray` | `#6b7280` | True gray |\n| `slate` | `#64748b` | Cool with blue tint |\n| `neutral` | `#737373` | Pure gray |\n\n### Context Token Resolution\n\nContent authors write bare palette references in `theme.yml` contexts:\n\n```yaml\ncontexts:\n  dark:\n    primary: primary-400\n    link: primary-300\n```\n\nThe processor resolves `primary-400` to `var(--primary-400)`. Plain CSS values (hex, `var()`, named colors) pass through unchanged.\n\n### Validation\n\n```javascript\nimport { validateThemeConfig } from '@uniweb/theming'\n\nconst { valid, errors } = validateThemeConfig({\n  colors: { primary: 'not-a-color' },\n})\n// valid: false\n// errors: ['Color \"primary\" has invalid value: not-a-color']\n```\n\n## CSS Generation\n\nGenerate complete CSS from a processed theme configuration.\n\n```javascript\nimport { generateThemeCSS } from '@uniweb/theming'\n\nconst css = generateThemeCSS(config)\n```\n\nThe output includes (in order):\n\n1. **Typography** — `@import` rules and `--font-body`, `--font-heading`, `--font-mono` variables\n2. **Color palettes** — `--primary-50` through `--primary-950` (and secondary, accent, neutral) on `:root`\n3. **Default semantic tokens** — `--heading`, `--body`, `--link`, `--border`, etc. on `:root`\n4. **Context classes** — `.context-light`, `.context-medium`, `.context-dark` with full token sets\n5. **Foundation variables** — Custom `--var-name` properties from foundation defaults + site overrides\n6. **Dark scheme** — `.scheme-dark` class and optional `prefers-color-scheme` media query\n7. **Site background** — `body { background: ... }` if specified\n8. **Inline text styles** — `span[accent]`, `span[muted]` for markdown inline styling\n\n### Semantic Tokens\n\nEach context class sets these CSS custom properties:\n\n| Token | Purpose |\n|---|---|\n| `--section` | Section background |\n| `--card` | Card/surface background |\n| `--muted` | Muted/disabled background |\n| `--body` | Body text |\n| `--heading` | Heading text |\n| `--subtle` | Secondary/muted text |\n| `--border` | Borders |\n| `--link` / `--link-hover` | Link colors |\n| `--primary` / `--primary-foreground` / `--primary-hover` / `--primary-border` | Primary button |\n| `--secondary` / `--secondary-foreground` / `--secondary-hover` / `--secondary-border` | Secondary button |\n| `--success` / `--warning` / `--error` / `--info` | Status colors |\n| `--ring` | Focus ring |\n\n### Inspecting Defaults\n\n```javascript\nimport { getDefaultContextTokens, getDefaultColors } from '@uniweb/theming'\n\ngetDefaultColors()\n// { primary: '#3b82f6', secondary: '#64748b', accent: '#8b5cf6', neutral: '#78716c' }\n\ngetDefaultContextTokens()\n// { light: { section: 'var(--neutral-50)', ... }, medium: { ... }, dark: { ... } }\n```\n\n## Foundation Integration\n\nFoundations declare customizable variables; sites set values in `theme.yml`. This package handles the merge.\n\n```javascript\nimport { extractFoundationVars, foundationHasVars } from '@uniweb/theming'\n\n// Check if a foundation declares theme variables\nfoundationHasVars(schemaJson)  // true/false\n\n// Extract vars from a foundation module\nconst vars = extractFoundationVars(await import('./foundation/vars.js'))\n```\n\n## How It Fits in Uniweb\n\n```\ntheme.yml → processTheme() → generateThemeCSS() → CSS injected at build time\n                                                      ↓\n                                          :root { --primary-500: ...; --heading: ...; }\n                                          .context-light { --section: var(--neutral-50); ... }\n                                          .context-dark  { --section: var(--neutral-900); ... }\n                                                      ↓\n                                          Runtime applies .context-{theme} per section\n                                                      ↓\n                                          Components use var(--heading), var(--link), etc.\n```\n\nThe site controls the theme. The foundation declares what's customizable. Components use semantic tokens and adapt to any context automatically.\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funiweb%2Ftheming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funiweb%2Ftheming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funiweb%2Ftheming/lists"}