@roxyapi/ui 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +1 -1
  2. package/dist/cdn/components/ashtakavarga-grid.js +3 -362
  3. package/dist/cdn/components/ashtakavarga-grid.js.map +2 -2
  4. package/dist/cdn/components/biorhythm-chart.js +2 -225
  5. package/dist/cdn/components/biorhythm-chart.js.map +2 -2
  6. package/dist/cdn/components/choghadiya-grid.js +2 -231
  7. package/dist/cdn/components/choghadiya-grid.js.map +2 -2
  8. package/dist/cdn/components/compatibility-card.js +2 -230
  9. package/dist/cdn/components/compatibility-card.js.map +2 -2
  10. package/dist/cdn/components/dasha-timeline.js +2 -282
  11. package/dist/cdn/components/dasha-timeline.js.map +2 -2
  12. package/dist/cdn/components/data.js +3 -230
  13. package/dist/cdn/components/data.js.map +2 -2
  14. package/dist/cdn/components/divisional-chart.js +34 -321
  15. package/dist/cdn/components/divisional-chart.js.map +4 -4
  16. package/dist/cdn/components/dosha-card.js +2 -225
  17. package/dist/cdn/components/dosha-card.js.map +2 -2
  18. package/dist/cdn/components/endpoint-form.js +2 -243
  19. package/dist/cdn/components/endpoint-form.js.map +2 -2
  20. package/dist/cdn/components/guna-milan.js +2 -269
  21. package/dist/cdn/components/guna-milan.js.map +2 -2
  22. package/dist/cdn/components/hexagram.js +3 -247
  23. package/dist/cdn/components/hexagram.js.map +2 -2
  24. package/dist/cdn/components/horoscope-card.js +3 -281
  25. package/dist/cdn/components/horoscope-card.js.map +2 -2
  26. package/dist/cdn/components/kp-chart.js +2 -277
  27. package/dist/cdn/components/kp-chart.js.map +2 -2
  28. package/dist/cdn/components/kp-planets-table.js +2 -195
  29. package/dist/cdn/components/kp-planets-table.js.map +2 -2
  30. package/dist/cdn/components/kp-ruling-planets.js +2 -240
  31. package/dist/cdn/components/kp-ruling-planets.js.map +2 -2
  32. package/dist/cdn/components/location-search.js +2 -240
  33. package/dist/cdn/components/location-search.js.map +2 -2
  34. package/dist/cdn/components/moon-phase.js +3 -223
  35. package/dist/cdn/components/moon-phase.js.map +2 -2
  36. package/dist/cdn/components/nakshatra-card.js +2 -200
  37. package/dist/cdn/components/nakshatra-card.js.map +2 -2
  38. package/dist/cdn/components/natal-chart.js +9 -560
  39. package/dist/cdn/components/natal-chart.js.map +4 -4
  40. package/dist/cdn/components/numerology-card.js +2 -232
  41. package/dist/cdn/components/numerology-card.js.map +2 -2
  42. package/dist/cdn/components/panchang-table.js +3 -220
  43. package/dist/cdn/components/panchang-table.js.map +2 -2
  44. package/dist/cdn/components/shadbala-table.js +3 -284
  45. package/dist/cdn/components/shadbala-table.js.map +2 -2
  46. package/dist/cdn/components/synastry-chart.js +5 -395
  47. package/dist/cdn/components/synastry-chart.js.map +2 -2
  48. package/dist/cdn/components/tarot-card.js +3 -261
  49. package/dist/cdn/components/tarot-card.js.map +2 -2
  50. package/dist/cdn/components/tarot-spread.js +2 -248
  51. package/dist/cdn/components/tarot-spread.js.map +2 -2
  52. package/dist/cdn/components/transits-table.js +3 -382
  53. package/dist/cdn/components/transits-table.js.map +4 -4
  54. package/dist/cdn/components/vedic-kundli.js +35 -271
  55. package/dist/cdn/components/vedic-kundli.js.map +4 -4
  56. package/dist/cdn/components/vedic-planets-table.js +2 -202
  57. package/dist/cdn/components/vedic-planets-table.js.map +2 -2
  58. package/dist/cdn/components/western-planets-table.js +3 -192
  59. package/dist/cdn/components/western-planets-table.js.map +2 -2
  60. package/dist/cdn/components/yoga-list.js +2 -305
  61. package/dist/cdn/components/yoga-list.js.map +2 -2
  62. package/dist/cdn/roxy-ui.js +41 -5059
  63. package/dist/cdn/roxy-ui.js.map +4 -4
  64. package/dist/cdn/widgets.js +1 -114
  65. package/dist/components/ashtakavarga-grid.js +1 -577
  66. package/dist/components/ashtakavarga-grid.js.map +3 -3
  67. package/dist/components/biorhythm-chart.js +1 -378
  68. package/dist/components/biorhythm-chart.js.map +3 -3
  69. package/dist/components/choghadiya-grid.js +1 -397
  70. package/dist/components/choghadiya-grid.js.map +3 -3
  71. package/dist/components/compatibility-card.js +1 -359
  72. package/dist/components/compatibility-card.js.map +3 -3
  73. package/dist/components/dasha-timeline.js +1 -447
  74. package/dist/components/dasha-timeline.js.map +3 -3
  75. package/dist/components/data.js +1 -408
  76. package/dist/components/data.js.map +3 -3
  77. package/dist/components/divisional-chart.d.ts.map +1 -1
  78. package/dist/components/divisional-chart.js +64 -929
  79. package/dist/components/divisional-chart.js.map +4 -4
  80. package/dist/components/dosha-card.js +1 -339
  81. package/dist/components/dosha-card.js.map +3 -3
  82. package/dist/components/endpoint-form.js +1 -505
  83. package/dist/components/endpoint-form.js.map +3 -3
  84. package/dist/components/guna-milan.js +1 -420
  85. package/dist/components/guna-milan.js.map +3 -3
  86. package/dist/components/hexagram.js +1 -426
  87. package/dist/components/hexagram.js.map +3 -3
  88. package/dist/components/horoscope-card.js +1 -427
  89. package/dist/components/horoscope-card.js.map +3 -3
  90. package/dist/components/kp-chart.js +1 -441
  91. package/dist/components/kp-chart.js.map +3 -3
  92. package/dist/components/kp-planets-table.js +1 -292
  93. package/dist/components/kp-planets-table.js.map +3 -3
  94. package/dist/components/kp-ruling-planets.js +1 -334
  95. package/dist/components/kp-ruling-planets.js.map +3 -3
  96. package/dist/components/location-search.js +1 -461
  97. package/dist/components/location-search.js.map +3 -3
  98. package/dist/components/moon-phase.js +1 -373
  99. package/dist/components/moon-phase.js.map +3 -3
  100. package/dist/components/nakshatra-card.js +1 -290
  101. package/dist/components/nakshatra-card.js.map +3 -3
  102. package/dist/components/natal-chart.d.ts +0 -1
  103. package/dist/components/natal-chart.d.ts.map +1 -1
  104. package/dist/components/natal-chart.js +8 -1084
  105. package/dist/components/natal-chart.js.map +4 -4
  106. package/dist/components/numerology-card.js +1 -361
  107. package/dist/components/numerology-card.js.map +3 -3
  108. package/dist/components/panchang-table.js +1 -396
  109. package/dist/components/panchang-table.js.map +3 -3
  110. package/dist/components/shadbala-table.js +1 -459
  111. package/dist/components/shadbala-table.js.map +3 -3
  112. package/dist/components/synastry-chart.js +7 -704
  113. package/dist/components/synastry-chart.js.map +3 -3
  114. package/dist/components/tarot-card.js +1 -379
  115. package/dist/components/tarot-card.js.map +3 -3
  116. package/dist/components/tarot-spread.js +1 -356
  117. package/dist/components/tarot-spread.js.map +3 -3
  118. package/dist/components/transits-table.d.ts +2 -0
  119. package/dist/components/transits-table.d.ts.map +1 -1
  120. package/dist/components/transits-table.js +1 -594
  121. package/dist/components/transits-table.js.map +4 -4
  122. package/dist/components/vedic-kundli.d.ts.map +1 -1
  123. package/dist/components/vedic-kundli.js +64 -845
  124. package/dist/components/vedic-kundli.js.map +4 -4
  125. package/dist/components/vedic-planets-table.js +1 -414
  126. package/dist/components/vedic-planets-table.js.map +3 -3
  127. package/dist/components/western-planets-table.js +1 -409
  128. package/dist/components/western-planets-table.js.map +3 -3
  129. package/dist/components/yoga-list.js +1 -429
  130. package/dist/components/yoga-list.js.map +3 -3
  131. package/dist/index.cjs +77 -8721
  132. package/dist/index.cjs.map +4 -4
  133. package/dist/index.js +77 -8701
  134. package/dist/index.js.map +4 -4
  135. package/dist/utils/disclosure.d.ts +16 -0
  136. package/dist/utils/disclosure.d.ts.map +1 -0
  137. package/dist/utils/kundli-render.d.ts.map +1 -1
  138. package/dist/utils/kundli-styles.d.ts.map +1 -1
  139. package/dist/utils/tablist.d.ts +44 -0
  140. package/dist/utils/tablist.d.ts.map +1 -0
  141. package/dist/version.d.ts +1 -1
  142. package/package.json +3 -1
  143. package/src/components/dasha-timeline.ts +7 -7
  144. package/src/components/divisional-chart.ts +2 -0
  145. package/src/components/natal-chart.ts +37 -62
  146. package/src/components/transits-table.ts +45 -18
  147. package/src/components/vedic-kundli.ts +2 -1
  148. package/src/utils/disclosure.ts +62 -0
  149. package/src/utils/kundli-render.ts +21 -35
  150. package/src/utils/kundli-styles.ts +0 -31
  151. package/src/utils/tablist.ts +124 -0
  152. package/src/version.ts +1 -1
@@ -0,0 +1,16 @@
1
+ import type { TemplateResult } from 'lit';
2
+ /**
3
+ * Disclosure chevron for `<details>`/`<summary>` accordions. Replaces the
4
+ * browser default triangle with a thin chevron that rotates when the host
5
+ * `<details>` opens. Render it inside the `<summary>` and pair with
6
+ * {@link disclosureStyles}.
7
+ */
8
+ export declare function chevron(): TemplateResult;
9
+ /**
10
+ * Shared `<details>` accordion styling: hides the native disclosure triangle
11
+ * and rotates the {@link chevron} 180 degrees when the accordion is open.
12
+ * Import into any component that renders disclosure accordions so the chevron
13
+ * looks and behaves identically.
14
+ */
15
+ export declare const disclosureStyles: import("lit").CSSResult;
16
+ //# sourceMappingURL=disclosure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disclosure.d.ts","sourceRoot":"","sources":["../../src/utils/disclosure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAG1C;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,cAAc,CAiBxC;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,yBA2B5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"kundli-render.d.ts","sourceRoot":"","sources":["../../src/utils/kundli-render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AA+B1C;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAsGpD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,CACX,MAAM,EACN;IACC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CACD,EACD,aAAa,CAAC,EAAE,MAAM,GACpB,eAAe,CAoBjB;AAmdD;;;;GAIG;AACH,wBAAgB,eAAe,CAC9B,EAAE,EAAE,eAAe,EACnB,KAAK,EAAE,UAAU,GACf,cAAc,CAShB;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACvC,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAClC,cAAc,CAkChB"}
1
+ {"version":3,"file":"kundli-render.d.ts","sourceRoot":"","sources":["../../src/utils/kundli-render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAgC1C;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAsGpD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,CACX,MAAM,EACN;IACC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CACD,EACD,aAAa,CAAC,EAAE,MAAM,GACpB,eAAe,CAoBjB;AA8dD;;;;GAIG;AACH,wBAAgB,eAAe,CAC9B,EAAE,EAAE,eAAe,EACnB,KAAK,EAAE,UAAU,GACf,cAAc,CAShB;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACvC,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAClC,cAAc,CAQhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"kundli-styles.d.ts","sourceRoot":"","sources":["../../src/utils/kundli-styles.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,yBA8GxB,CAAC"}
1
+ {"version":3,"file":"kundli-styles.d.ts","sourceRoot":"","sources":["../../src/utils/kundli-styles.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,yBA+ExB,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { TemplateResult } from 'lit';
2
+ /** A single tab in a {@link renderTablist} strip. */
3
+ export interface TablistItem<T extends string = string> {
4
+ id: T;
5
+ label: string;
6
+ }
7
+ /**
8
+ * Shared styling for every horizontal tab strip in the library (natal chart
9
+ * views, transits, kundli styles). Keeping one rule set means the tabs look
10
+ * identical everywhere and theme through the same --roxy-* tokens.
11
+ */
12
+ export declare const tablistStyles: import("lit").CSSResult;
13
+ /**
14
+ * Render a WAI-ARIA tablist. The host component owns the active-tab state; this
15
+ * helper draws the buttons, wires click plus Left/Right arrow navigation with a
16
+ * roving tabindex, and moves focus to the newly selected tab. Pair with
17
+ * {@link tablistStyles}.
18
+ *
19
+ * Pass `controls: true` when each tab governs a sibling
20
+ * `<div role="tabpanel" id="${idPrefix}-panel-${id}">` so the buttons advertise
21
+ * `aria-controls`. Omit it for tablists that swap a single rendered view in
22
+ * place with no separate panel element (the kundli style switch).
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * renderTablist({
27
+ * items: [{ id: 'wheel', label: 'Wheel' }, { id: 'grid', label: 'Aspect grid' }],
28
+ * active: this.view,
29
+ * onSelect: (v) => { this.view = v; },
30
+ * label: 'Natal chart views',
31
+ * idPrefix: 'natal',
32
+ * controls: true,
33
+ * })
34
+ * ```
35
+ */
36
+ export declare function renderTablist<T extends string>(opts: {
37
+ items: ReadonlyArray<TablistItem<T>>;
38
+ active: T;
39
+ onSelect: (id: T) => void;
40
+ label: string;
41
+ idPrefix: string;
42
+ controls?: boolean;
43
+ }): TemplateResult;
44
+ //# sourceMappingURL=tablist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tablist.d.ts","sourceRoot":"","sources":["../../src/utils/tablist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAG1C,qDAAqD;AACrD,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACrD,EAAE,EAAE,CAAC,CAAC;IACN,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,yBAgCzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE;IACrD,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,CAAC,CAAC;IACV,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,cAAc,CA6CjB"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const ROXY_UI_VERSION = "0.5.0";
1
+ export declare const ROXY_UI_VERSION = "0.6.1";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@roxyapi/ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Web components for the RoxyAPI catalog. Drop-in charts, tables, cards, forms for astrology, tarot, numerology, biorhythm, I Ching, crystals, dreams, angel numbers, and more. One key, beautiful in 30 minutes.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
+ "jsdelivr": "./dist/cdn/roxy-ui.js",
10
+ "unpkg": "./dist/cdn/roxy-ui.js",
9
11
  "exports": {
10
12
  ".": {
11
13
  "types": "./dist/index.d.ts",
@@ -134,18 +134,18 @@ export class RoxyDashaTimeline extends LitElement {
134
134
  font-variant-numeric: tabular-nums;
135
135
  text-align: right;
136
136
  }
137
- details.interp {
137
+ .interp {
138
138
  border: 1px solid var(--roxy-border, #e4e4e7);
139
139
  border-radius: var(--roxy-radius-md, 8px);
140
140
  padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
141
141
  background: var(--roxy-bg, #fff);
142
142
  }
143
- details.interp summary {
144
- cursor: pointer;
143
+ .interp h3 {
144
+ margin: 0;
145
145
  font-size: var(--roxy-text-sm, 0.875rem);
146
146
  font-weight: var(--roxy-weight-bold, 600);
147
147
  }
148
- details.interp p {
148
+ .interp p {
149
149
  margin: var(--roxy-space-sm, 0.5rem) 0 0;
150
150
  font-size: var(--roxy-text-sm, 0.875rem);
151
151
  color: var(--roxy-muted, #71717a);
@@ -232,10 +232,10 @@ export class RoxyDashaTimeline extends LitElement {
232
232
  private renderActiveInterpretation(periods: DashaPeriod[]) {
233
233
  const active = periods.find((p) => this.isCurrent(p));
234
234
  if (!active?.interpretation) return nothing;
235
- return html`<details class="interp">
236
- <summary>${active.planet} mahadasha interpretation</summary>
235
+ return html`<div class="interp">
236
+ <h3>${active.planet} mahadasha</h3>
237
237
  <p>${active.interpretation}</p>
238
- </details>`;
238
+ </div>`;
239
239
  }
240
240
 
241
241
  private renderCurrent(d: DashaData) {
@@ -12,6 +12,7 @@ import {
12
12
  } from '../utils/kundli-render.js';
13
13
  import { kundliStyles } from '../utils/kundli-styles.js';
14
14
  import { MarkupDataController } from '../utils/markup-data.js';
15
+ import { tablistStyles } from '../utils/tablist.js';
15
16
 
16
17
  /**
17
18
  * Divisional chart renderer (D2-D60). Accepts a DivisionalChartResponse and
@@ -26,6 +27,7 @@ export class RoxyDivisionalChart extends LitElement {
26
27
  static styles = [
27
28
  baseStyles,
28
29
  kundliStyles,
30
+ tablistStyles,
29
31
  css`
30
32
  .division-meta {
31
33
  font-size: var(--roxy-text-sm, 0.875rem);
@@ -15,6 +15,7 @@ import {
15
15
  oppositePoint,
16
16
  polarToCartesian,
17
17
  } from '../utils/degree.js';
18
+ import { chevron, disclosureStyles } from '../utils/disclosure.js';
18
19
  import {
19
20
  ASPECT_CLASS,
20
21
  formatNumber,
@@ -22,6 +23,7 @@ import {
22
23
  } from '../utils/format.js';
23
24
  import { MarkupDataController } from '../utils/markup-data.js';
24
25
  import { capitalize } from '../utils/string.js';
26
+ import { renderTablist, tablistStyles } from '../utils/tablist.js';
25
27
 
26
28
  type PlanetEntry = NatalChartResponse['planets'][number];
27
29
  type AspectEntry = NatalChartResponse['aspects'][number];
@@ -43,6 +45,8 @@ const ANGLE_LABEL_R = 196;
43
45
  export class RoxyNatalChart extends LitElement {
44
46
  static styles = [
45
47
  baseStyles,
48
+ tablistStyles,
49
+ disclosureStyles,
46
50
  css`
47
51
  .wrap {
48
52
  width: 100%;
@@ -190,32 +194,6 @@ export class RoxyNatalChart extends LitElement {
190
194
  vertical-align: middle;
191
195
  }
192
196
 
193
- .tablist {
194
- display: flex;
195
- gap: 2px;
196
- border-bottom: 2px solid var(--roxy-border, #e4e4e7);
197
- }
198
- .tab {
199
- padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
200
- font-size: var(--roxy-text-sm, 0.875rem);
201
- background: none;
202
- border: none;
203
- border-bottom: 2px solid transparent;
204
- margin-bottom: -2px;
205
- cursor: pointer;
206
- color: var(--roxy-muted, #71717a);
207
- font-family: inherit;
208
- transition: color var(--roxy-motion-duration, 200ms) var(--roxy-motion-easing, ease);
209
- }
210
- .tab[aria-selected='true'] {
211
- color: var(--roxy-accent-fg, #b45309);
212
- border-bottom-color: var(--roxy-accent, #f59e0b);
213
- font-weight: var(--roxy-weight-bold, 600);
214
- }
215
- .tab:hover:not([aria-selected='true']) {
216
- color: var(--roxy-fg, #0a0a0a);
217
- }
218
-
219
197
  .grid-scroll {
220
198
  overflow-x: auto;
221
199
  -webkit-overflow-scrolling: touch;
@@ -356,10 +334,18 @@ export class RoxyNatalChart extends LitElement {
356
334
  cursor: pointer;
357
335
  font-weight: 500;
358
336
  color: var(--roxy-fg, #0f172a);
337
+ display: flex;
338
+ align-items: center;
339
+ justify-content: space-between;
340
+ gap: var(--roxy-space-md, 1rem);
341
+ }
342
+ .interp-aside {
343
+ display: inline-flex;
344
+ align-items: center;
345
+ gap: 0.6em;
359
346
  }
360
- .interp-card summary small {
347
+ .interp-aside small {
361
348
  color: var(--roxy-muted, #71717a);
362
- margin-left: 0.5em;
363
349
  font-weight: 400;
364
350
  }
365
351
  .interp-body {
@@ -438,29 +424,24 @@ export class RoxyNatalChart extends LitElement {
438
424
  : nothing
439
425
  }
440
426
  </header>
427
+ ${renderTablist({
428
+ items: [
429
+ { id: 'wheel', label: 'Wheel' },
430
+ { id: 'grid', label: 'Aspect grid' },
431
+ ],
432
+ active: view,
433
+ onSelect: (v) => {
434
+ this.view = v;
435
+ },
436
+ label: 'Natal chart views',
437
+ idPrefix: 'natal',
438
+ controls: true,
439
+ })}
441
440
  <div
442
- class="tablist"
443
- role="tablist"
444
- aria-label="Natal chart views"
445
- @keydown=${this.onTabKeyDown}
441
+ id="natal-panel-${view}"
442
+ role="tabpanel"
443
+ aria-labelledby="natal-tab-${view}"
446
444
  >
447
- ${(['wheel', 'grid'] as const).map(
448
- (t) => html`<button
449
- class="tab"
450
- role="tab"
451
- id="tab-${t}"
452
- aria-selected=${view === t ? 'true' : 'false'}
453
- aria-controls="panel-${t}"
454
- tabindex=${view === t ? '0' : '-1'}
455
- @click=${() => {
456
- this.view = t;
457
- }}
458
- >
459
- ${t === 'wheel' ? 'Wheel' : 'Aspect grid'}
460
- </button>`,
461
- )}
462
- </div>
463
- <div id="panel-${view}" role="tabpanel" aria-labelledby="tab-${view}">
464
445
  ${view === 'wheel' ? this.renderWheel(planets, aspects) : this.renderAspectGrid(planets, aspects)}
465
446
  </div>
466
447
  <div class="legend">
@@ -479,18 +460,6 @@ export class RoxyNatalChart extends LitElement {
479
460
  </div>`;
480
461
  }
481
462
 
482
- private onTabKeyDown(e: KeyboardEvent) {
483
- if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
484
- e.preventDefault();
485
- this.view = this.view === 'wheel' ? 'grid' : 'wheel';
486
- const next = this.view;
487
- requestAnimationFrame(() => {
488
- this.shadowRoot
489
- ?.querySelector<HTMLButtonElement>(`#tab-${next}`)
490
- ?.focus();
491
- });
492
- }
493
-
494
463
  private renderWheel(planets: PlanetEntry[], aspects: AspectEntry[]) {
495
464
  return html`<svg
496
465
  viewBox="0 0 ${SIZE} ${SIZE}"
@@ -880,7 +849,13 @@ export class RoxyNatalChart extends LitElement {
880
849
  const glyph = PLANET_GLYPH[capitalize(p.name)] ?? '';
881
850
  const deg = formatNumber(p.degree ?? 0, 1);
882
851
  return html`<details class="interp-card" name="natal-planet-readings" ?open=${idx === 0}>
883
- <summary>${glyph} ${p.name} <small>${p.sign ?? ''} ${deg}</small></summary>
852
+ <summary>
853
+ <span>${glyph} ${p.name}</span>
854
+ <span class="interp-aside">
855
+ <small>${p.sign ?? ''} ${deg}</small>
856
+ ${chevron()}
857
+ </span>
858
+ </summary>
884
859
  <div class="interp-body">
885
860
  ${interp.summary ? html`<p class="interp-summary">${interp.summary}</p>` : nothing}
886
861
  ${interp.detailed ? html`<p class="interp-detail">${interp.detailed}</p>` : nothing}
@@ -1,11 +1,13 @@
1
1
  import { css, html, LitElement, nothing } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
2
+ import { customElement, property, state } from 'lit/decorators.js';
3
3
  import { PLANET_GLYPH, SIGN_GLYPH } from '../tokens/index.js';
4
4
  import type { TransitsResponse } from '../types/index.js';
5
5
  import { baseStyles } from '../utils/base-styles.js';
6
+ import { chevron, disclosureStyles } from '../utils/disclosure.js';
6
7
  import { formatDate, formatNumber, formatTime } from '../utils/format.js';
7
8
  import { MarkupDataController } from '../utils/markup-data.js';
8
9
  import { capitalize } from '../utils/string.js';
10
+ import { renderTablist, tablistStyles } from '../utils/tablist.js';
9
11
 
10
12
  /**
11
13
  * Transit positions and aspect table. Pass `data` from /astrology/transits.
@@ -16,6 +18,8 @@ import { capitalize } from '../utils/string.js';
16
18
  export class RoxyTransitsTable extends LitElement {
17
19
  static styles = [
18
20
  baseStyles,
21
+ tablistStyles,
22
+ disclosureStyles,
19
23
  css`
20
24
  .wrap {
21
25
  display: grid;
@@ -226,6 +230,10 @@ export class RoxyTransitsTable extends LitElement {
226
230
  @property({ attribute: false })
227
231
  data: TransitsResponse | null = null;
228
232
 
233
+ /** Which panel is showing: planet positions or the transit-to-natal aspects. */
234
+ @state()
235
+ private tab: 'positions' | 'aspects' = 'positions';
236
+
229
237
  render() {
230
238
  if (!this.data?.transitPlanets?.length) {
231
239
  return html`<div class="roxy-empty" role="status">No transits data</div>`;
@@ -242,31 +250,49 @@ export class RoxyTransitsTable extends LitElement {
242
250
  const dateStr = [formatDate(transitDate), formatTime(transitTime)]
243
251
  .filter(Boolean)
244
252
  .join(' ');
253
+ const aspectCount = transitAspects?.length ?? 0;
254
+ const tab = this.tab;
245
255
 
246
- return html`<div class="wrap" aria-label="Transit positions table">
256
+ return html`<div class="wrap" aria-label="Transits">
247
257
  <div class="head">
248
258
  <h2 class="title">Transits</h2>
249
259
  ${dateStr ? html`<p class="subtitle">${dateStr}</p>` : nothing}
250
260
  </div>
251
261
 
252
- ${summary ? this.renderSummaryPills(summary) : nothing}
253
-
254
- <div>
255
- <p class="section-label">Planet positions</p>
256
- <div class="overflow-scroll">
257
- ${this.renderPlanetsTable(transitPlanets)}
258
- </div>
259
- </div>
260
-
261
262
  ${
262
- transitAspects?.length
263
- ? html`<div>
264
- <p class="section-label">Transit aspects</p>
265
- <div class="overflow-scroll">
266
- ${this.renderAspectsList(transitAspects)}
267
- </div>
263
+ aspectCount > 0
264
+ ? html`${renderTablist({
265
+ items: [
266
+ { id: 'positions', label: 'Positions' },
267
+ { id: 'aspects', label: `Aspects (${aspectCount})` },
268
+ ],
269
+ active: tab,
270
+ onSelect: (v) => {
271
+ this.tab = v;
272
+ },
273
+ label: 'Transit views',
274
+ idPrefix: 'transits',
275
+ controls: true,
276
+ })}
277
+ <div
278
+ id="transits-panel-${tab}"
279
+ role="tabpanel"
280
+ aria-labelledby="transits-tab-${tab}"
281
+ >
282
+ ${
283
+ tab === 'positions'
284
+ ? html`<div class="overflow-scroll">
285
+ ${this.renderPlanetsTable(transitPlanets)}
286
+ </div>`
287
+ : html`${summary ? this.renderSummaryPills(summary) : nothing}
288
+ <div class="overflow-scroll">
289
+ ${this.renderAspectsList(transitAspects ?? [])}
290
+ </div>`
291
+ }
292
+ </div>`
293
+ : html`<div class="overflow-scroll">
294
+ ${this.renderPlanetsTable(transitPlanets)}
268
295
  </div>`
269
- : nothing
270
296
  }
271
297
  </div>`;
272
298
  }
@@ -355,6 +381,7 @@ export class RoxyTransitsTable extends LitElement {
355
381
  <span class="meta">
356
382
  ${status} · orb ${formatNumber(a.orb, 2)}° · strength ${formatNumber(a.strength, 1)}
357
383
  </span>
384
+ ${chevron()}
358
385
  </summary>
359
386
  <div class="interp-body">
360
387
  ${interp?.summary ? html`<p>${interp.summary}</p>` : nothing}
@@ -11,6 +11,7 @@ import {
11
11
  } from '../utils/kundli-render.js';
12
12
  import { kundliStyles } from '../utils/kundli-styles.js';
13
13
  import { MarkupDataController } from '../utils/markup-data.js';
14
+ import { tablistStyles } from '../utils/tablist.js';
14
15
 
15
16
  /**
16
17
  * Vedic kundli (D1 Rashi chart). Pass `data` from /vedic-astrology/birth-chart.
@@ -28,7 +29,7 @@ import { MarkupDataController } from '../utils/markup-data.js';
28
29
  */
29
30
  @customElement('roxy-vedic-kundli')
30
31
  export class RoxyVedicKundli extends LitElement {
31
- static styles = [baseStyles, kundliStyles];
32
+ static styles = [baseStyles, kundliStyles, tablistStyles];
32
33
 
33
34
  constructor() {
34
35
  super();
@@ -0,0 +1,62 @@
1
+ import type { TemplateResult } from 'lit';
2
+ import { css, html } from 'lit';
3
+
4
+ /**
5
+ * Disclosure chevron for `<details>`/`<summary>` accordions. Replaces the
6
+ * browser default triangle with a thin chevron that rotates when the host
7
+ * `<details>` opens. Render it inside the `<summary>` and pair with
8
+ * {@link disclosureStyles}.
9
+ */
10
+ export function chevron(): TemplateResult {
11
+ return html`<svg
12
+ class="roxy-chevron"
13
+ viewBox="0 0 16 16"
14
+ width="14"
15
+ height="14"
16
+ aria-hidden="true"
17
+ >
18
+ <path
19
+ d="M4 6l4 4 4-4"
20
+ fill="none"
21
+ stroke="currentColor"
22
+ stroke-width="1.5"
23
+ stroke-linecap="round"
24
+ stroke-linejoin="round"
25
+ />
26
+ </svg>`;
27
+ }
28
+
29
+ /**
30
+ * Shared `<details>` accordion styling: hides the native disclosure triangle
31
+ * and rotates the {@link chevron} 180 degrees when the accordion is open.
32
+ * Import into any component that renders disclosure accordions so the chevron
33
+ * looks and behaves identically.
34
+ */
35
+ export const disclosureStyles = css`
36
+ summary {
37
+ list-style: none;
38
+ }
39
+ summary::-webkit-details-marker {
40
+ display: none;
41
+ }
42
+ /* Explicit size: components that draw a chart set a global svg { width: 100% }
43
+ rule, and an element selector beats the SVG width/height attributes. This
44
+ class selector wins back the 14px icon size. */
45
+ .roxy-chevron {
46
+ flex-shrink: 0;
47
+ width: 14px;
48
+ height: 14px;
49
+ aspect-ratio: auto;
50
+ color: var(--roxy-muted, #71717a);
51
+ transition: transform var(--roxy-motion-duration, 200ms)
52
+ var(--roxy-motion-easing, ease);
53
+ }
54
+ details[open] > summary .roxy-chevron {
55
+ transform: rotate(180deg);
56
+ }
57
+ @media (prefers-reduced-motion: reduce) {
58
+ .roxy-chevron {
59
+ transition: none;
60
+ }
61
+ }
62
+ `;
@@ -1,8 +1,9 @@
1
1
  import type { TemplateResult } from 'lit';
2
- import { html, nothing, svg } from 'lit';
2
+ import { nothing, svg } from 'lit';
3
3
  import { PLANET_ABBR, SIGN_ABBR, SIGNS_ORDER } from '../tokens/index.js';
4
4
  import { longitudeToSignPosition } from './degree.js';
5
5
  import { capitalize } from './string.js';
6
+ import { renderTablist } from './tablist.js';
6
7
 
7
8
  /**
8
9
  * Canonical viewBox geometry for every kundli style. The chart is drawn into a
@@ -320,6 +321,11 @@ function renderSouthCell(
320
321
  ? svg`<text class="house-num" x=${r.x + r.w - 6} y=${r.y + 12} text-anchor="end" dominant-baseline="central">${houseNum}</text>`
321
322
  : nothing
322
323
  }
324
+ ${
325
+ isLagna
326
+ ? svg`<text class="lagna-marker" x=${cx} y=${r.y + 26} text-anchor="middle" dominant-baseline="central">Asc</text>`
327
+ : nothing
328
+ }
323
329
  ${planets.length ? renderPlanetStack(planets, sign, cx, cy + 4, 14) : nothing}
324
330
  </g>
325
331
  `;
@@ -453,9 +459,15 @@ function renderNorthCell(
453
459
  // always stays comfortably inside its triangle or diamond.
454
460
  const rashiOffsetY = Math.min(14, Math.abs(c.y - CENTRE) * 0.45 + 6);
455
461
  const ascOffsetY = rashiOffsetY + 12;
462
+ // North cells carry only a rasi number by convention. The ascendant also
463
+ // names its sign so the reader can see which sign rises without translating
464
+ // the number; other cells stay number-only.
465
+ const rashiLabel = isLagna
466
+ ? `${rashiNum} · ${SIGN_ABBR[sign] ?? sign.slice(0, 2)}`
467
+ : `${rashiNum}`;
456
468
  return svg`
457
469
  <g class=${isLagna ? 'cell lagna' : 'cell'}>
458
- <text class="rashi-num" x=${c.x} y=${c.y - rashiOffsetY} text-anchor="middle" dominant-baseline="central">${rashiNum}</text>
470
+ <text class="rashi-num" x=${c.x} y=${c.y - rashiOffsetY} text-anchor="middle" dominant-baseline="central">${rashiLabel}</text>
459
471
  ${
460
472
  isLagna
461
473
  ? svg`<text class="lagna-marker" x=${c.x} y=${c.y - ascOffsetY} text-anchor="middle" dominant-baseline="central">Asc</text>`
@@ -705,37 +717,11 @@ export function renderKundliStyleTablist(
705
717
  active: ChartStyle,
706
718
  setStyle: (next: ChartStyle) => void,
707
719
  ): TemplateResult {
708
- const onKeyDown = (e: KeyboardEvent) => {
709
- const idx = CHART_STYLES.findIndex((s) => s.id === active);
710
- if (e.key === 'ArrowRight') {
711
- e.preventDefault();
712
- const next = CHART_STYLES[(idx + 1) % CHART_STYLES.length];
713
- if (next) setStyle(next.id);
714
- } else if (e.key === 'ArrowLeft') {
715
- e.preventDefault();
716
- const next =
717
- CHART_STYLES[(idx - 1 + CHART_STYLES.length) % CHART_STYLES.length];
718
- if (next) setStyle(next.id);
719
- }
720
- };
721
- return html`<div
722
- class="kundli-tablist"
723
- role="tablist"
724
- aria-label="Kundli style"
725
- @keydown=${onKeyDown}
726
- >
727
- ${CHART_STYLES.map(
728
- (s) => html`<button
729
- type="button"
730
- class="kundli-tab"
731
- role="tab"
732
- id="kundli-tab-${s.id}"
733
- aria-selected=${active === s.id ? 'true' : 'false'}
734
- tabindex=${active === s.id ? '0' : '-1'}
735
- @click=${() => setStyle(s.id)}
736
- >
737
- ${s.label}
738
- </button>`,
739
- )}
740
- </div>`;
720
+ return renderTablist({
721
+ items: CHART_STYLES,
722
+ active,
723
+ onSelect: setStyle,
724
+ label: 'Kundli style',
725
+ idPrefix: 'kundli',
726
+ });
741
727
  }
@@ -28,37 +28,6 @@ export const kundliStyles = css`
28
28
  font-weight: var(--roxy-weight-bold, 600);
29
29
  margin: 0;
30
30
  }
31
- .kundli-tablist {
32
- display: inline-flex;
33
- gap: 2px;
34
- border-bottom: 2px solid var(--roxy-border, #e4e4e7);
35
- }
36
- .kundli-tab {
37
- padding: var(--roxy-space-xs, 0.25rem) var(--roxy-space-md, 1rem);
38
- font-size: var(--roxy-text-sm, 0.875rem);
39
- background: none;
40
- border: none;
41
- border-bottom: 2px solid transparent;
42
- margin-bottom: -2px;
43
- cursor: pointer;
44
- color: var(--roxy-muted, #71717a);
45
- font-family: inherit;
46
- transition: color var(--roxy-motion-duration, 200ms)
47
- var(--roxy-motion-easing, ease);
48
- }
49
- .kundli-tab[aria-selected='true'] {
50
- color: var(--roxy-accent-fg, #b45309);
51
- border-bottom-color: var(--roxy-accent, #f59e0b);
52
- font-weight: var(--roxy-weight-bold, 600);
53
- }
54
- .kundli-tab:hover:not([aria-selected='true']) {
55
- color: var(--roxy-fg, #0a0a0a);
56
- }
57
- .kundli-tab:focus-visible {
58
- outline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));
59
- outline-offset: 2px;
60
- border-radius: 4px;
61
- }
62
31
  svg {
63
32
  display: block;
64
33
  width: 100%;