feat(core): derive every brand token from the agency color by contrast - #204
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
SishirP17
force-pushed
the
fix/color-mix-tints
branch
from
August 18, 2026 22:52
ca08e60 to
051b143
Compare
SishirP17
force-pushed
the
feat/agency-theme-resolver
branch
from
August 18, 2026 22:55
a2fca5e to
ca897cf
Compare
applyAgencyTheme overrode three variables and left everything else at the
RayHealth defaults. --color-on-brand stayed white forever, so a pale agency
brand color would produce white-on-pale text app-wide; accentColor was read
from the database and never applied; and the set and clear paths listed
their variables separately, so they could drift.
theme-resolver.ts derives the whole brand token set as a pure function of
the agency's colors. Two properties do the work:
- The foreground is chosen, not assumed. --color-on-brand is whichever of
white or slate-900 scores higher, measured against every stop of
--gradient-brand rather than against --color-primary alone.
- The fill moves, not the ink. A color that cannot carry a label is
deepened until it can, and the agency's literal color is preserved as
--color-brand-raw for logo marks and other text-free surfaces, so
branding survives.
Alpha tints solve for their composited result, so --color-sidebar-active
lowers its alpha until white still clears AA over the dark rail, and the
focus ring raises its alpha until it is visible on the surface.
resolveAgencyTheme is total: {}, null, garbage, and attempted CSS injection
all return a complete AA-clean set. Every emitted value is built from
numbers, so no caller-supplied text reaches the CSSOM. 3,753 assertions
cover 416 brand colors including pure white, pure black, and mid-grey,
which fails against both white and black ink.
agencyThemeSchema now validates the colors instead of typing them as bare
strings, and findTheme salvages field by field so a legacy row with one bad
hex keeps its brand name rather than being dropped whole.
The web bundle deep-imports the resolver rather than the package barrel,
which re-exports every repository and would drag knex, pg, and
ssh2-sftp-client into the browser. vitest.config.ts needs its own alias
because it replaces vite.config.ts rather than merging with it.
This is also the first time packages/web imports from packages/core, so web
now declares the dependency. It resolved fine locally through the vite
alias and the tsconfig project reference, but turbo builds its graph from
package.json, so `^build` never built core before web's typecheck and CI
failed with TS6305 on a missing core/dist. Verified from a cold state with
core/dist and the turbo cache both removed.
SishirP17
force-pushed
the
feat/agency-theme-resolver
branch
from
August 18, 2026 23:00
ca897cf to
6031d51
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
applyAgencyThemeoverrode three variables and left everything else at the RayHealth defaults:--color-on-brandstayed#FFFFFFforever, so a pale agency brand color would produce white-on-pale text app-wide.accentColorwas read from the database and never applied.This is the part that makes a future home-care agency's color scheme safe rather than lucky.
How it works
theme-resolver.tsderives all 25 brand tokens as a pure function of the agency's colors. Two properties do the work:--color-on-brandis whichever of white or slate-900 scores higher — measured against every stop of--gradient-brand, not just--color-primary, because a gradient-filled button shows all three.--color-brand-rawfor logo marks and other text-free surfaces, so the branding survives.Alpha tints solve for their composited result:
--color-sidebar-activelowers its alpha until white still clears AA over the dark rail, and the focus ring raises its alpha until it is actually visible on the surface.resolveAgencyThemeis total —{},null, garbage, and attempted CSS injection all return a complete AA-clean set. Every emitted value is built from numbers, so no caller-supplied text reaches the CSSOM. 3,753 assertions over 416 brand colors, including pure white, pure black, and mid-grey#808080, which fails against both white and black ink.Boundary
agencyThemeSchemavalidates the colors instead of typing them as barez.string().findThemesalvages field by field rather than all-or-nothing, so a legacy row with one bad hex keeps its brand name — read-time normalization, no migration.Two things worth knowing
knex,pg, andssh2-sftp-clientinto the browser. Verified absent from the built bundle.vitest.config.tsneeds its own@rayhealth/corealias because it replacesvite.config.tsrather than merging with it.Third of five, stacked on #203.