You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(i18n,theming): a form wrote English over every field, and a dark select opened white on white
Two visitor-facing defects and the guards that would have caught them.
FORM LABELS. Every label came from a wire name through humanize(), so a Spanish page
rendered `Birth date` over a translated city box, and no catalogue keyed on English source
text could reach it: that text is itself computed here, so there is no constant to key on.
The API now publishes the map at /languages/field-labels, keyed by wire name with enums as
{field}.{value}. It is pulled at BUILD time by scripts/sync-field-labels.ts, which
`bun run generate` calls right after it refreshes the spec, because they are one fact from
one source.
Build time, not run time, and the first answer was the other way round. A runtime fetch was
argued as decoupling language from the release chain, but this form already consumes
version-pinned schema slices baked at build, so there was no decoupling to protect, and on an
embed it would paint English and then re-render translated a moment later. The labels ride
the locales/{lang}.js payload a translated page already downloads: no extra request, English
still ships nothing, works with no network at render time.
humanize() stays as the FALLBACK, which is what makes partial locales shippable: an
untranslated language, a key a translator declined to guess, a blocked request and a page
with no payload are one code path, not four.
DARK MODE. `color-scheme` was never declared anywhere in the library, so the browser painted
every native control light. A <select> looked right closed and opened a light popup whose
<option> rows inherited our near-white --roxy-fg: white on white. Same omission dimmed the
date and time pickers, the number spinner, the autofill highlight and the scrollbar. Declared
once per theme trigger beside the tokens, because a per-control fix cannot reach the popup at
all: the OS renders it outside the DOM.
DATES. formatDateGrain passed a bare date straight to new Date(), which the platform reads as
UTC midnight and Intl renders in the VIEWER's zone, so everyone west of Greenwich read the
previous day. Normalisation moved into resolveDisplayDate and three inline copies went with
it.
GUARDS. THEMING.md is now asserted against tokens.css (every token documented, none invented,
every printed hex correct) and caught three real defects: --roxy-ring and --roxy-accent-ink
were documented as hardcoded hex when both DERIVE from the accent, so the doc told a themer
to override what already follows their brand. The layout e2e now asserts document-level
scroll, and the untranslated-literal ratchet holds every file not in its budget at zero, so a
new component cannot start out leaking.
RELEASE. scripts/purge-cdn.ts purges both jsDelivr aliases and then POLLS x-jsd-version until
every asset serves the published version, as a separate verify-cdn job so a CDN hiccup cannot
strand the packages on npm with no tag or release. Publishing is not shipping: 0.27.2 and
0.27.3 sat half-live for twelve hours with npm correct and nothing erroring.
Copy file name to clipboardExpand all lines: AGENTS.md
+33-64Lines changed: 33 additions & 64 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -215,51 +215,6 @@ Several components select a view, mode, or chart layout in addition to `data`. T
215
215
216
216
The full set: `RoxyNatalChart``houseSystem`, `RoxyHoroscopeCard``period`, `RoxyMoonPhase``mode`, `RoxyCompatibilityCard``mode`, `RoxyVedicKundli` and `RoxyDivisionalChart``chartStyle`, `RoxyPanchangTable``detail`, `RoxyDashaTimeline``period`, `RoxyDoshaCard``type`, `RoxyNumerologyCard``type`, `RoxyTarotSpread``spread`, `RoxyBiorhythmChart``mode`, `RoxyHexagram``mode`. Outside React and Vue, set the same value as a kebab-case attribute or a JS property on the element (for example `chart-style="south"` or `el.chartStyle = 'south'`).
217
217
218
-
### 6d. Chart without the written report
219
-
220
-
Every component takes `hide-readings` (`hideReadings` in React and Vue). It renders the chart and the data and leaves the interpretation out of the page: wheels, maps, tables, grids, legends, badges and every number stay, the interpretive prose goes. Off by default, so a component that does not set it is unchanged.
221
-
222
-
The line is drawn on the CONTENT, never on the block, so a block that reads like analysis still stays when it is made of measurements. `<roxy-natal-chart>` and `<roxy-aspects-table>` both render the `patterns` payload and both make the same cut: the figure name, element, modality, tightness and planet chips are geometry and survive, the paragraph under them is the reading and goes. To remove such a block entirely, use `hide-sections` below.
Reach for it when the page supplies its own words, which is the usual case for a practitioner site that writes its own commentary under the chart. The prose is left out of the markup rather than hidden with CSS, so the page never ships text it is not showing.
233
-
234
-
**Which components act on it.** Every component that renders a written interpretation, so there is no tag you have to test to find out. A pure chart or table has no interpretation to take away, so the attribute is a no-op on those by definition.
235
-
236
-
One component is a documented no-op despite being mostly prose:
237
-
238
-
| Component | Behaviour |
239
-
|---|---|
240
-
|`<roxy-dream-card>`| Ignores `hide-readings`. The dream symbol response is the symbol, its dictionary letter and the interpretation, so removing the interpretation would leave a heading over nothing. Style it with `::part(card)` or leave the card out of the page instead. |
241
-
242
-
What survives, by family: charts keep the drawing, legend, glyphs, degrees and tab strip; tables keep every row and every calculated column (kaksha bindus, koota scores, significance bars, orbs, strengths); cards keep the header, badges, meters and fact grids, so a dosha keeps its verdict, phase and severity, a crystal keeps its Mohs hardness and attribute grid, and a horoscope keeps its energy meter, Moon placement, active transits and key dates; every Vedic response keeps its sidereal frame caption. What goes: interpretation paragraphs, reading accordions, keyword chips attached to a reading, remedies, action steps and strengths lists, and any section whose only content was one of those, heading included.
243
-
244
-
### 6e. Removing a whole block
245
-
246
-
Every component also takes `hide-sections` (`hideSections` in React and Vue): a comma-separated list of `part` names, and each one named is taken off that component.
**It is per element, which is the difference from a stylesheet.** A `::part()` rule in your CSS reaches every matching component on the site at once; this reaches the one you put it on, so the same chart can keep its patterns on one page and drop them on another with no CSS at all.
258
-
259
-
**Use `hide-readings` when the words must not ship, and `hide-sections` when a block should not show.** They are different tools rather than two spellings of one. `hide-readings` drops interpretive prose out of the markup entirely, so the page never ships text it is not displaying. `hide-sections` hides the block with CSS and leaves it in the DOM, because a block is a rendering concern and its content may be measurements you have no reason to strip.
260
-
261
-
Any name a component publishes works, and the names are in `components-catalog.json` under `parts`. A name the component does not carry hides nothing and is not an error, so one list can be applied across a page of mixed components. Names are case-insensitive and spaces around the commas are ignored.
262
-
263
218
### 6c. Vue and Nuxt
264
219
265
220
`@roxyapi/ui-vue` exposes the same components with the same prop names. Bind `data` and the config props normally; the package sets them as DOM properties for you, so an object payload never gets stringified into an attribute. Listen to widget events with the usual `@` syntax.
@@ -607,20 +562,37 @@ Per-element scope is supported:
607
562
608
563
Every visible aspect of the chart is driven by `--roxy-*` CSS custom properties on `:host`. Override any token on `:root`, on `:host`, or per element. Do not write Tailwind utility classes inside the components; the Shadow DOM boundary stops them at the door.
609
564
610
-
## Restyling a block from outside the component
565
+
## Choosing what a component renders
611
566
612
-
Tokens cover colour, spacing and type. When you need to reach a specific block, every chart and reading component names its structural pieces with `part`, so a stylesheet on the page can target them. The names are identical in every component, so one rule covers the library.
567
+
Three tools, one vocabulary. Every structural block carries a `part` name; `::part()` targets it from a stylesheet, `hide-sections` drops it on one element, and `hide-readings` removes interpretive prose from the markup entirely.
613
568
614
-
```css
615
-
roxy-natal-chart::part(readings) {
616
-
display: none;
617
-
}
618
-
roxy-natal-chart::part(card) {
619
-
border: 0;
620
-
box-shadow: none;
621
-
}
569
+
**Pick by what you need:**
570
+
571
+
| Need | Use |
572
+
|---|---|
573
+
| Restyle a block |`::part(name)` in your CSS |
574
+
| Hide a block everywhere on the site |`::part(name) { display: none }`|
575
+
| Hide a block on ONE element, keep it on another |`hide-sections="name"`|
576
+
| Keep the chart, drop the written interpretation |`hide-readings`|
577
+
578
+
`hide-readings` and `hide-sections` are different tools, not two spellings of one. `hide-readings` leaves the prose out of the markup, so the page never ships text it is not displaying. `hide-sections` hides the block whatever it holds, because a block may be measurements you have no reason to strip.
Both are off by default. `hide-sections` takes a comma-separated list, is case-insensitive, ignores spaces around commas, and a name the component does not carry hides nothing and is not an error, so one list can be applied across a page of mixed components.
591
+
592
+
**The line `hide-readings` draws is on the CONTENT, never on the block**, so a block that reads like analysis stays when it is made of measurements. `<roxy-natal-chart>` and `<roxy-aspects-table>` both render the `patterns` payload and both make the same cut: figure name, element, modality, tightness and planet chips are geometry and survive; the paragraph under them is the reading and goes. To remove the block itself, that is what `hide-sections` is for. What survives elsewhere: charts keep the drawing, legend, glyphs, degrees and tab strip; tables keep every calculated column (kaksha bindus, koota scores, orbs, strengths); cards keep header, badges, meters and fact grids; every Vedic response keeps its sidereal frame caption. One documented no-op: `<roxy-dream-card>` ignores `hide-readings`, because the response IS the interpretation and removing it would leave a heading over nothing.
593
+
594
+
### The part vocabulary
595
+
624
596
| part | What it names |
625
597
|---|---|
626
598
|`card`| The component's own card or wrapper root |
|`form`, `loading`, `error`, `edit-bar`, `attribution`| The built-in states, on every component |
637
609
638
-
The table is the shared vocabulary, not the whole list. **`components-catalog.json` carries a `parts` array for every component, so you can read the exact names a component answers to instead of guessing or inspecting the DOM.** A name means the same block wherever it appears, which is what makes one rule enough: `::part(aspects)` reaches the aspect grid on a natal chart, the aspect list on an aspects table and the transit aspects on a transits table alike.
639
-
640
-
Two notes. Parts reach exactly one shadow root deep, and a component that draws another one re-exports its parts, so `roxy-relocation-wheel::part(readings)` reaches the wheel it nests. And to remove a reading rather than hide it, use `hide-readings` (above): a `display: none` rule still ships the words in the page.
610
+
That table is the shared vocabulary, not the whole list. **`components-catalog.json` carries a `parts` array for every component, so read the exact names a component answers to instead of guessing or inspecting the DOM.** A name means the same block wherever it appears, which is what makes one rule enough: `::part(aspects)` reaches the aspect grid on a natal chart, the aspect list on an aspects table and the transit aspects on a transits table alike. Parts reach exactly one shadow root deep, and a component that draws another re-exports its parts, so `roxy-relocation-wheel::part(readings)` reaches the wheel it nests.
641
611
642
612
## Rules every agent must follow
643
613
@@ -651,7 +621,7 @@ Two notes. Parts reach exactly one shadow root deep, and a component that draws
651
621
652
622
## Domain ordering
653
623
654
-
When listing domains in user-visible copy, use the canonical order: Western astrology, Vedic astrology, numerology, tarot, human design, forecast, biorhythm, I Ching, crystals, dreams, angel numbers. Location is utility, not a selling domain.
624
+
When listing domains in user-visible copy, use the canonical order: Western astrology, Vedic astrology, forecast, human design, numerology, tarot, biorhythm, I Ching, crystals, dreams, angel numbers. Location is utility, not a selling domain. The `domain` field in `components-catalog.json` carries the same names, so sort by this list rather than by whatever order you discovered components in.
655
625
656
626
## What not to ship
657
627
@@ -666,9 +636,8 @@ When listing domains in user-visible copy, use the canonical order: Western astr
666
636
667
637
## Where to look next
668
638
669
-
- Component source: `packages/ui/src/components/`
670
-
- Sample data for every component: `apps/docs/sample-data.js`
671
-
- Token reference: `packages/ui/THEMING.md`
672
-
- Live preview: `bun run preview` then open `http://localhost:3001`
639
+
-**Machine-readable catalog, and the one to reach for first**: <https://cdn.jsdelivr.net/npm/@roxyapi/ui@latest/components-catalog.json>. Every component with its `tag`, `pascal` name, `domain`, what it renders, the `endpoints` it consumes, its `parts`, and copy-paste `snippets`. Fetch and query it instead of scraping the tables in this file.
640
+
- Token reference: `THEMING.md`, beside this file in the package
- Machine-readable component catalog (every component, its domain, what it renders, and the endpoint(s) it consumes): <https://cdn.jsdelivr.net/npm/@roxyapi/ui@latest/components-catalog.json>. Fetch it to discover or map components programmatically instead of scraping this table.
The parts: `card`, `header`, `chart`, `legend`, `details`, `table`, `tablist`, `tab`, `panel`, `section`, `readings`, `reading`, plus `form`, `loading`, `error` and `attribution` on the built-in states. A `section` also carries its own name, which is what `::part(patterns)` above targets, so any single block can be dropped or restyled on its own.
739
+
The parts: `card`, `header`, `chart`, `legend`, `details`, `table`, `tablist`, `tab`, `panel`, `section`, `readings`, `reading`, plus `form`, `loading`, `error`, `edit-bar` and `attribution` on the built-in states. A `section` also carries its own name, which is what `::part(patterns)` above targets, so any single block can be dropped or restyled on its own.
740
+
741
+
That list is the shared vocabulary, not the whole set. **[`components-catalog.json`](https://cdn.jsdelivr.net/npm/@roxyapi/ui@latest/components-catalog.json) carries a `parts` array for every component**, so you can read the exact names a component answers to instead of guessing or inspecting the DOM. A name means the same block wherever it appears, which is what makes one rule enough: `::part(aspects)` reaches the aspect grid on a natal chart, the aspect list on an aspects table and the transit aspects on a transits table alike.
742
+
743
+
One nesting rule worth knowing: parts reach exactly one shadow root deep, and a component that draws another one re-exports its parts, so `roxy-relocation-wheel::part(readings)` reaches the wheel nested inside it.
740
744
741
745
### Chart without the written report
742
746
@@ -752,7 +756,7 @@ For the same outcome in markup rather than CSS, set `hide-readings`. The compone
752
756
753
757
It is off by default, so nothing changes until you ask for it. Wheels, maps, tables, grids, legends, badges and every number stay; the interpretive prose is what goes.
754
758
755
-
That line is drawn on the content, not on the block, so a block made of measurements stays even when it reads like analysis. The clearest case is the natal chart patterns block: a T-Square or a Stellium is a geometric fact about where the bodies sit, so the figure, its element and modality, its tightness and its planets all survive, and only the paragraph under them goes. To drop the block itself, hide its part: `roxy-natal-chart::part(patterns) { display: none }`.
759
+
That line is drawn on the content, not on the block, so a block made of measurements stays even when it reads like analysis. The clearest case is the natal chart patterns block: a T-Square or a Stellium is a geometric fact about where the bodies sit, so the figure, its element and modality, its tightness and its planets all survive, and only the paragraph under them goes. To drop the block itself, use `hide-sections` below.
756
760
757
761
#### Which components it applies to
758
762
@@ -773,6 +777,25 @@ What each family keeps when you set it:
773
777
774
778
What goes: interpretation paragraphs, the reading accordions, keyword chips attached to a reading, remedies and action steps and strengths lists (sentences laid out as bullets), and any section whose only content was one of those, heading included.
775
779
780
+
### Remove a whole block
781
+
782
+
Every component also takes `hide-sections` (`hideSections` in React and Vue): a comma-separated list of `part` names, and each one named is taken off that component.
The names are the same `part` names listed above, so anything you can target with `::part()` you can also drop with an attribute. The rule is generated from the name rather than from a list of components that opted in, so it works on every component, and a name no block carries hides nothing rather than erroring.
794
+
795
+
**Use `hide-readings` when the words must not ship, and `hide-sections` when a block should not show.** They are different tools rather than two spellings of one. `hide-readings` drops interpretive prose out of the markup entirely, so the page never ships text it is not displaying. `hide-sections` hides the block and leaves it in the DOM, because a block is a rendering concern and its content may be measurements you have no reason to strip.
796
+
797
+
**Reach for `::part()` instead when one rule should cover a whole site**, and for `hide-sections` when two components on the same page have to differ, which a stylesheet cannot express.
798
+
776
799
## Languages
777
800
778
801
A component reads the page language and renders its own labels in it. Set `lang` on the element, on any ancestor of it, or on `<html>`, and add the catalogue for that language.
0 commit comments