angora-design-system-audit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited angora-design-system-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
src/components/), a design system page (src/pages/design-system/), a layout (src/pages/design-system/layouts/), or a site page (src/pages/*.astro)? Some rules only apply to specific scopes (marked below). Layouts are full-page compositions — audit them like site pages (Page Rules apply) but note they use placeholder content, so skip SEO checks.Then validate the target against the rules below.
| Rule | Check |
|---|---|
| Token compliance | No arbitrary values. All styling via Tailwind utility classes referencing theme tokens |
| Semantic token enforcement | No raw palette utility classes. Components and pages must use only semantic token classes (bg-background, bg-card, text-foreground, text-muted-foreground, border-border, bg-primary, etc.). Grep for violations: bg-gray-*, text-gray-*, border-gray-*, bg-white, text-white, bg-black, text-black, bg-primary-[0-9], text-primary-[0-9], border-primary-[0-9] (raw scale references). The primitive palette lives outside @theme so these classes shouldn't exist — but inline styles, arbitrary values (bg-[#...], text-[var(--gray-*)]), and accidental palette references still need catching. Exception: style guide page (index.astro) uses inline style attributes with var(--gray-50) etc. for palette swatches — this is intentional |
| Pixel translation | No hard-coded pixel values in components. User-provided pixel values should map to the nearest theme token (e.g., "32px" → spacing-8 / p-8). If no token fits, use rem for sizing and em for prose-relative spacing. Flag any arbitrary [Xpx] values |
| Scoping | Component styles via Tailwind classes on elements. No global CSS selectors in component files |
| No true black | Never #000, rgb(0,0,0), or black. Use semantic tokens (text-foreground, bg-foreground) which resolve to near-black |
| No generated colors | No color-mix(), lighten(), darken(), or rgba() for text on colored backgrounds. All colors from semantic tokens or explicit palette values |
| Rule | Check |
|---|---|
| Component naming | Component names must follow the generic UI pattern, not a use case. Flag components named after their content (e.g., FAQ instead of Accordion, Testimonials instead of Carousel). The name should be reusable |
data-component with flow attributes | Every component's root element has data-component="Name" (PascalCase). Sub-components too (data-component="CardBody"). Attribute appears first in the attribute list. Landmark components also set flow participation attributes (data-seamless, data-full-width) on their root element |
| Landmark components | Landmark components (Hero, Footer, Nav) live on their own without a Section. They render their own semantic element (<section>, <footer>, <nav>) with data-component and flow attributes. Flag landmarks wrapped in <Section> |
| Content components | Content components render a @container wrapper div as their root so container queries work anywhere. They do NOT compose Section internally. Flag content components that import or render Section internally |
| Heading ownership | Content components must not accept a heading or title prop — a parent <Section title="..."> provides the section heading. Content headings that are part of internal layout are fine as sub-components |
| No outer vertical margin | Landmark and content components never set margin-top/margin-bottom on their outermost element. That's the flow context's (section-flow/prose-flow) job. Flag any mt-*/mb-*/my-* on a component's root |
| Content props on composite shells | Composite shells must have zero content props — content flows exclusively through sub-components. Flag any string or array prop on a composite shell that represents displayable content (headings, descriptions, legal text, nav links). Even "simple" string props must be sub-components. Landmarks are not exempt |
| No visual content via props | Visual content flows via children and sub-components, never as props. Flag any prop that passes markup or rendered content (named slots used to pass styled markup into the shell where a sub-component should be used instead). The shell's only job is layout and behavior — content ownership belongs to sub-components |
Images as <img> | All imagery uses <img> elements with object-fit: cover where needed. No CSS background-image for content images (heroes, cards, full-bleed sections). Position with absolute inset-0 inside a relative container for backdrops |
| States | All components interactive by default (pseudo-class variants for hover:, active:, focus-visible: + transitions). Form state prop only for non-interactive states (error, success, disabled). Composites: own variants only, child primitives in default state |
Contrast ratios and ARIA labeling are covered by the automated a11y test suite (pnpm test:a11y). When dark mode exists, tests run against both /view/light/* and /view/dark/* routes — each mode must independently meet AA. The audit focuses on what automated testing can't catch:
| Rule | Check |
|---|---|
| Target sizes | Interactive targets ≥ 44px. Check at narrow widths too |
| Color independence | Don't rely on color alone to convey meaning — always a secondary indicator (icon, text, border, pattern) |
| EAA extras (manual) | If standard is EAA: verify text spacing override tolerance, 200% text resize. (lang and reflow are covered by the a11y test) |
| Dark mode contrast | If dark mode exists: verify semantic token pairs provide sufficient contrast in both modes. Check that --color-*-foreground has ≥ 4.5:1 contrast against its paired --color-* in both light and dark values. The a11y test catches most issues, but token-level mismatches (e.g., light primary text on light primary bg) need manual verification when new tokens are added |
| Rule | Check |
|---|---|
| Semantic elements | Content uses semantic elements (section, nav, h1–h6, p, a, button, input, figure, blockquote, ul/ol, footer). No <div> where a semantic element fits |
| Heading hierarchy | Heading levels don't skip (no h1 → h3). Components accept an as prop or similar for heading level flexibility |
| Rule | Check |
|---|---|
| Container query syntax | Responsive variants use @sm:, @md:, @lg:, @xl: (container queries). No @media / no viewport-based sm:/md:/lg: in component markup. Only exception: design-system.css (tooling) |
| Responsive behavior | Component actually adapts at narrow (~320px), medium (~768px), and wide (~1280px) container widths. Check: multi-column layouts stack to single column; text doesn't overflow or clip; interactive targets stay ≥ 44px; images/media scale without breaking aspect ratio; spacing reduces proportionally at narrow widths |
@container ancestor | Content components provide their own @container wrapper. Section provides @container on its root <section> element. For inline content inside Section, Section's @container is sufficient. Flag content components that use container query variants (@sm:, @md:, @lg:) but don't have their own @container wrapper |
| Rule | Check |
|---|---|
| Three-tier emphasis | Clear primary/secondary/tertiary emphasis using size + weight + color together |
| Three-tier text color | Exactly three text color tiers: text-foreground (or text-card-foreground) for primary content, text-muted-foreground for secondary/tertiary. Flag any raw grey class usage — semantic tokens enforce the correct tiers. If a third visual tier is needed, use font size or weight rather than an additional color |
| Font weight discipline | Only two weights: normal (font-normal/font-medium, 400–500) and bold (font-semibold/font-bold, 600–700). Flag font-thin, font-extralight, font-light (under 400) |
| Label treatment | Labels should be de-emphasized (smaller, lighter, thinner). Combine labels and values when possible ("12 left in stock" not "In stock: 12") |
| Grey on colored backgrounds | No grey text on colored backgrounds. On primary/destructive/colored surfaces, use the paired -foreground token (e.g., text-primary-foreground on bg-primary). Never use opacity-based text on colored backgrounds — it looks washed out |
| Rule | Check |
|---|---|
| Proximity grouping | Related elements closer together, groups further apart. No ambiguous spacing where the relationship between elements is unclear |
| Generous whitespace | Err on the side of generous whitespace. Flag areas that feel cramped — especially padding inside cards, space between section heading and content |
| Rule | Check |
|---|---|
| Line length | 45–75 characters. Use max-w-[Xch] (prefer ch units) on text containers. Constrain paragraphs independently if container has full-width elements |
| Line height | Proportional to font size: 1.5–1.75 for body text, 1.0–1.2 for display/heading text. Wider content may need up to 2.0 |
text-wrap: balance | Headings (h1–h4, display text) should have text-wrap: balance to prevent orphan lines. The prose utility applies this automatically |
text-wrap: pretty | Body text (p, li, blockquote) should have text-wrap: pretty to prevent single-word last lines. The prose utility applies this automatically |
| Letter-spacing | Headlines should tighten tracking (tracking-tight or tracking-tighter). Small uppercase UI text (badges, eyebrows, nav labels under text-lg) should widen (tracking-wide). Display-size uppercase text (text-xl and above) keeps normal tracking — the large size already provides legibility. See system.md Conventions for the full letter-spacing policy |
| Center-align limits | Center-aligned text only for very short blocks (1–3 lines). Body copy and longer text must be left-aligned. Flag centered paragraphs |
| Tabular numbers | Numbers in tables, stats, and vertically-aligned contexts should use tabular-nums (font-variant-numeric: tabular-nums) |
| Rule | Check |
|---|---|
| Meaning, not decoration | Color communicates meaning. Greys for structure, color for emphasis and action |
| Semantic color usage | Red for errors/destructive, yellow/amber for warnings, green for success. Don't repurpose semantic colors for decoration |
| Rule | Check |
|---|---|
| Consistent application | Similar elements at the same elevation. Light source from above. In dark mode, tonal elevation (lighter surface = higher) conveys depth — shadows have reduced visibility |
| Shadow interaction | Interactive elevated elements: hover increases shadow (lift), active decreases shadow (press). Flag elevated buttons/cards without shadow transitions |
| Fewer borders | Prefer box shadows, different background colors, or extra spacing over borders for visual separation. Flag heavy border usage where a subtler technique would work |
src/pages/*.astro and src/pages/design-system/layouts/)Skip this section when auditing components or design system specimen pages.
| Rule | Check |
|---|---|
| Flow context | Pages use <main class="section-flow"> for inter-section spacing. Posts/articles use <article class="prose-flow"> |
| Manual typography in content zones | Flag manual typography classes on <h2>, <p>, <a> inside content zones — prose handles these. Only flag inside section-flow or prose-flow zones where ambient prose applies |
| Blockquote prose protection | Blockquotes inside prose zones need protection via data-component (prose targets blockquote:not([data-component])) or explicit counter-declarations (border-l-0 pl-0 not-italic) |
| List prose protection | UI lists (navigation, tags, non-content lists) inside prose zones must have list-none p-0 to override prose's list styles. Content lists that should have bullets need no override |
| Landmark vs content wrapping | Landmark components (Hero, Footer, Nav) live on their own in section-flow — don't wrap them in <Section>. Content components sit inside <Section> |
| Section component | Content sections on the page use the Section component. No raw <section> elements with manual padding for content sections |
| Seamless usage | Sections with background colors/images use <Section seamless> (or the landmark's flow attributes). Adjacent seamless sections should butt up (0 gap) |
| Background rhythm | Page has visual rhythm — some variation in section backgrounds (alternating light/dark, or strategic use of color). Flag a page where every section is the same plain background |
| Composition purity — single file | Design system layout directories (src/pages/design-system/layouts/) must contain only one .astro file. Flag extra files |
| Composition purity — zero classes | In layouts and site pages, no Tailwind utility classes on raw HTML elements. Only section-flow on <main> is allowed. If a <div> needs a class, a component is missing |
| Composition purity — no client code | Flag inline <script> tags in layouts and site pages. Use Preact islands via components instead |
| Composition drift | When auditing a layout that has a corresponding site page (or vice versa), check for structural divergence — different section order, missing/extra sections, different component usage. Copy differences are expected (placeholder vs real content). Structural differences are a bug |
| SEO | Site pages (not layouts) have <title>, <meta name="description">. Template-driven pages wire SEO from table fields (meta_title, meta_description). Flag missing OG tags on public-facing pages |
system.md Anti-Patterns section.prose is applied at the <main> level as an ambient typography baseline. Its descendant selectors use :where() for low specificity, so any Tailwind class on a component naturally overrides it. The only local prose usage is for nested content zones that need their own vertical rhythm. Components (cards, heroes) should NOT use prose — they own their spacing explicitly via gap-* classes.Report all violations with: file path, line number, rule category, rule name, and suggested fix.
Severity levels:
Group findings by file, then by severity. Fix all errors and warnings before showing components to the user.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.