Icon system upgrade: token scale, alias dedup, brand redesign, PWA/OG assets, a11y rule#519
Conversation
Introduce a documented icon glyph size scale in the @theme block of globals.css (size-icon-xs..xl → 12/14/16/20/24px), parallel to how --spacing-tap generates size-tap. This retires the convention-only h-4 w-4 literal and lets a step carry a responsive variant (size-icon-md sm:size-icon-lg). Adds scripts/check-icon-scale.mjs (report mode) wired into verify:cheap. It flags unambiguous drift — the retired 4.5 (18px) half-step and arbitrary 12–24px icon sizes — but deliberately does NOT ban raw h-4 w-4, which also sizes non-icons (ToggleSwitch knob, status dots). Documents the scale in design-system.md §2 (Type & icon scale). Additive only: no call sites migrated yet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Five deprecated↔renamed alias families were used side-by-side. Collapse each onto one canonical name, verified against lucide-react@1.22.0 as the same icon module (zero-pixel): AlertTriangle → TriangleAlert AlertCircle → CircleAlert CheckCircle2 → CircleCheck XCircle → CircleX HelpCircle → CircleHelp 102 whole-word renames across 24 production files. Mockups keep their imports (out of scope). typecheck / lint / prettier green; no call site renders differently. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…--strict Resolve every h-4.5/w-4.5 (18px) icon size — the one off-4px-grid drift in the icon scale. Lucide glyphs snap to size-icon-lg (20px, preserving their "bigger than default" intent); non-icon 18px boxes (account-setup indicator / grid) snap to h-5; the two responsive sites tokenize as size-icon-md sm:size-icon-lg. One arbitrary h-[18px] ShieldCheck glyph also moves to size-icon-lg. Simplify check-icon-scale.mjs to enforce the 4.5 half-step only (dropped the arbitrary-px rule, which false-flagged non-icon 18px count badges) and flip it to --strict in verify:cheap so the half-step can't return. Verified: strict guard green, Tailwind emits .size-icon-* from the --spacing-icon-* tokens; typecheck / lint / prettier / 1648 vitest green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adopt the "Refined Pulse" direction: one clean, optically-centred QRS
spike, no trailing dot, slightly rounder tile. Define the geometry once
in src/lib/brand-mark.ts and derive every surface from it so the mark
can't drift across the favicon, the in-app <BrandMark>, app/icon.svg,
and (next) the app-icon / OG image routes:
- BrandMark renders the exported geometry as token-themed JSX
(var(--clinical-accent) → light/dark/forced-colors adaptive).
- app/icon.svg is generated by scripts/generate-brand-assets.ts;
brand:check (wired into verify:cheap) guards it from drift, the
same pattern as sitemap:check.
- brandMarkSvg()/brandMarkInner() are ready for the PR5 image routes.
favicon.ico (a multi-res binary) still carries the previous mark: the
toolchain has no rasteriser, so it needs offline regeneration (noted in
the script header and design-system.md §10). The SVG icon is primary in
modern browsers.
typecheck / lint / prettier / brand:check green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fill the app-icon asset gaps, all derived from the single brand-mark
source via next/og ImageResponse (no committed binaries):
- app/apple-icon.tsx (180x180, opaque teal) — fixes the
appleWebApp.capable:true mismatch (iOS was falling back to a page
screenshot for "Add to Home Screen").
- app/icons/[variant] — PWA icon set: icon-192/512 (transparent
"any") + maskable-192/512 (full-bleed teal, mark inside the ~72%
safe zone).
- app/manifest.ts — installable PWA; icons reference the SVG + PNG
set; theme/background match the light default.
- app/opengraph-image.tsx (1200x630) — branded link unfurls; text
uses ImageResponse's bundled default font (no font asset needed).
- layout.tsx: metadataBase (NEXT_PUBLIC_SITE_URL, localhost fallback)
so OG image URLs resolve absolutely.
Verified with a production build: all eight routes prerender; inspected
the generated apple / maskable / any / OG PNGs — the mark renders
correctly and the maskable safe-zone is respected. typecheck / lint /
prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PanelHeading (section header) and EmptyState glyphs now scale size-icon-md → sm:size-icon-lg: a compact 16px on mobile, 20px from the sm breakpoint up for desktop hierarchy. Glyph-only inside fixed tiles, so no layout shift. With the composer (globals.css media queries) and the mode-home hero already responsive, this covers the four high-value roles the plan scoped; body / badge / table icons stay fixed by design. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Correct icons that didn't match their concept or the mode registry:
- documentFacetIcons: Medication Target→Pill (matches prescribing),
Service Sparkles→ShieldCheck (Sparkles is the answer glyph; match
the services mode), Population FileText→Users.
- fallbackIconByType (favourites): forms FileText→FileSignature via
appModeIcons.forms, so a favourited form matches the forms mode.
Higher-visibility / clinical choices (services & differentials mode
glyphs, and differentiating the medication risk/contra/safe sections
that currently share ShieldCheck) are surfaced for review rather than
changed unilaterally. typecheck / lint / prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sites) Add a local ESLint rule (eslint-rules/require-lucide-icon-aria.mjs, wired into eslint.config.mjs as an error, mockups exempt): a lucide-react icon rendered as JSX must declare aria-hidden (decorative) or an accessible name (aria-label / aria-labelledby / role / title). Enforces the codebase's own convention so a decorative glyph can't silently reach the accessibility tree, and prevents regressions. The rule is auto-fixable; `eslint --fix` added aria-hidden="true" to 267 decorative icons across 33 files. Safe by construction: a bare lucide <svg> has no accessible name, so hiding it never strips a control's name (an icon-only button lacking a label was already unlabeled — a separate, runtime-axe concern, not one this change can regress). Verified: lint / typecheck / prettier / 1648 vitest / production build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…view-393584 # Conflicts: # src/components/DocumentViewer.tsx
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
Warning Review limit reached
Next review available in: 1 minute Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (58)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3319417d71
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
CI lints with --max-warnings 0. The inline {/* eslint-disable-next-line
@next/next/no-img-element */} placed inside a JSX expression container
does not work as a directive — it was reported unused while the rule
still flagged the <img>, failing the zero-warnings gate. Switch to the
file-level /* eslint-disable @next/next/no-img-element */ the codebase
already uses for these next/og image routes (cf. answer-content.tsx,
DocumentViewer.tsx). Verified with `npm run lint -- --max-warnings 0`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The file-level /* eslint-disable @next/next/no-img-element */ was reported "unused" on CI's fresh checkout but "used" locally: the Next plugin only fires this rule when a local .next build dir is present, so the directive flipped between used/unused and tripped --max-warnings 0. Move it to a deterministic eslint.config.mjs override (rule off for src/lib/brand-image.tsx + src/app/opengraph-image.tsx). A config rule setting can't be an "unused directive", so it holds in every environment. Verified with .next removed + `npm run lint -- --max-warnings 0`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…afety (#523) Follow-up to #519, applying the icon-gallery review items: - Services mode: ShieldCheck → Route. The mode is "service records + referral pathways"; ShieldCheck read as safety/trust. Changed at the single source (app-mode-icons.ts, so nav/search/favourites follow) plus the matching documentFacetIcons "Service" entry. - Medication record sections differentiated (risk/contra/safe all shared ShieldCheck): risk → TriangleAlert, contra → Ban, safe → ShieldCheck. Aligned with the app's semantic-tone safety icons (warning/danger); "contra" already carries a danger tone (medications.ts), which Ban matches. - Differentials kept as BrainCircuit: evaluated GitCompare (reads as a dev/git glyph in a clinical app) and Stethoscope (already used for "Clinical role" in settings) — neither beats BrainCircuit for distinctiveness + clinical legibility. Verified: prettier, lint --max-warnings 0, typecheck, check:icon-scale, brand:check green; no snapshot tests reference these maps (CI runs the full vitest + build gate). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Icon system upgrade — review, normalize, redesign, optimise
A full pass over the app's icons: normalize the set, tokenize sizing, redesign the brand mark, fill the app/PWA/social asset gaps, and enforce icon a11y. The system was already disciplined (one library, clean registries), so this is normalize + optimise + gap-fill, delivered as small, reviewable commits.
What changed
--spacing-icon-*in@theme(12/14/16/20/24px) generatingsize-icon-*utilities (parallel to--spacing-tap→size-tap), plus acheck-icon-scaleguard inverify:cheap.AlertTriangle→TriangleAlert,CheckCircle2→CircleCheck, …), verified as the same icon module inlucide-react@1.22.0.4.5/18px half-step; guard flipped to--strict.src/lib/brand-mark.ts→ drivesBrandMark,app/icon.svg(codegen-guarded viabrand:check), and the image routes below.next/og(no committed binaries):app/apple-icon.tsx(fixes theappleWebApp.capablemismatch),app/icons/[variant](192/512 + maskable),app/manifest.ts(installable PWA),app/opengraph-image.tsx, andmetadataBase.size-icon-md sm:size-icon-lg); composer & mode-home hero already scale.MedicationTarget→Pill,ServiceSparkles→ShieldCheck,Population→Users; favouriteforms→FileSignature.aria-hiddenor an accessible name;eslint --fixaddedaria-hiddento 267 decorative icons. Safe by construction (a bare lucide<svg>has no accessible name).Verification
npm run verify:cheapgreen (runtime, github-actions, sitemap, brand:check, type-scale, icon-scale, lint, typecheck), 1657 vitest pass, production build green (all image routes prerender; generated apple/maskable/OG PNGs inspected), and live Chromium screenshots at desktop + mobile. Merged currentmain(incl. #512's type-scale fix) and re-applied the alias + aria conventions to the merged code.Clinical governance
N/A — pure UI / iconography / brand work. No changes to ingestion, answer generation, search/ranking, source governance, document-access logic, privacy, production config, or clinical output. The
DocumentViewerchange isaria-hidden-only; document-search facet icons are presentation.Open for follow-up (left unchanged, not blocking)
Two mode glyphs (
Services: ShieldCheck vs Route/Waypoints;Differentials: BrainCircuit vs GitCompare/Stethoscope) and the medicationrisk/contra/safesections that shareShieldCheck— surfaced for a design/clinical call rather than changed unilaterally.🤖 Generated with Claude Code