reflex-silverpoint-react 0.6.0__tar.gz → 0.7.0__tar.gz

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 (40) hide show
  1. {reflex_silverpoint_react-0.6.0/custom_components/reflex_silverpoint_react.egg-info → reflex_silverpoint_react-0.7.0}/PKG-INFO +185 -11
  2. reflex_silverpoint_react-0.6.0/PKG-INFO → reflex_silverpoint_react-0.7.0/README.md +177 -32
  3. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/__init__.py +85 -0
  4. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/assets/surface.jsx +219 -0
  5. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/assets/surfaces.css +375 -0
  6. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/assets/timeline.css +225 -0
  7. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/assets/timeline.jsx +64 -0
  8. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/base.py +36 -0
  9. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/base.pyi +159 -0
  10. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/charts.pyi +126 -3597
  11. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/composed.py +702 -0
  12. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/dashboard.py +6 -1
  13. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/dashboard.pyi +179 -0
  14. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/overlays.py +835 -0
  15. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/palette.py +362 -0
  16. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/surfaces.py +329 -0
  17. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/surfaces.pyi +364 -0
  18. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/timeline.py +157 -0
  19. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/timeline.pyi +72 -0
  20. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/ui.py +16 -0
  21. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react/ui.pyi +942 -0
  22. reflex_silverpoint_react-0.6.0/README.md → reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react.egg-info/PKG-INFO +206 -6
  23. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react.egg-info/SOURCES.txt +13 -1
  24. reflex_silverpoint_react-0.7.0/custom_components/reflex_silverpoint_react.egg-info/requires.txt +11 -0
  25. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/pyproject.toml +14 -6
  26. reflex_silverpoint_react-0.7.0/tests/test_v07.py +234 -0
  27. reflex_silverpoint_react-0.6.0/custom_components/reflex_silverpoint_react/base.pyi +0 -274
  28. reflex_silverpoint_react-0.6.0/custom_components/reflex_silverpoint_react/dashboard.pyi +0 -314
  29. reflex_silverpoint_react-0.6.0/custom_components/reflex_silverpoint_react/ui.pyi +0 -1687
  30. reflex_silverpoint_react-0.6.0/custom_components/reflex_silverpoint_react.egg-info/requires.txt +0 -7
  31. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/LICENSE +0 -0
  32. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/catalog.py +0 -0
  33. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/charts.py +0 -0
  34. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/helpers.py +0 -0
  35. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react/ui_catalog.py +0 -0
  36. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react.egg-info/dependency_links.txt +0 -0
  37. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/custom_components/reflex_silverpoint_react.egg-info/top_level.txt +0 -0
  38. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/setup.cfg +0 -0
  39. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/tests/test_components.py +0 -0
  40. {reflex_silverpoint_react-0.6.0 → reflex_silverpoint_react-0.7.0}/tests/test_demo_data.py +0 -0
@@ -1,27 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex-silverpoint-react
3
- Version: 0.6.0
4
- Summary: The 60 silverpoint charts, the linked Dashboard and the 17 UI components (Renaissance silverpoint drawing style) for Reflex, wrapping @silverpoint/react
3
+ Version: 0.7.0
4
+ Summary: The 60 silverpoint charts, the Dashboard, 17 UI components, palettes, 20 composed components and a Knight Lab timeline (Renaissance silverpoint drawing style) for Reflex, wrapping @silverpoint/react
5
5
  Author-email: Ernesto Crespo <ecrespo@gmail.com>
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/ecrespo/reflex-silverpoint-react
8
8
  Project-URL: Source, https://github.com/ecrespo/reflex-silverpoint-react
9
9
  Project-URL: Issues, https://github.com/ecrespo/reflex-silverpoint-react/issues
10
10
  Project-URL: Upstream, https://github.com/ecrespo/silverpoint
11
- Keywords: reflex,reflex-custom-components,charts,silverpoint,data-visualization,svg,ui-components
11
+ Keywords: reflex,reflex-custom-components,charts,silverpoint,data-visualization,svg,ui-components,palette,timeline
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Topic :: Scientific/Engineering :: Visualization
16
- Requires-Python: >=3.10
16
+ Requires-Python: >=3.11
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: reflex>=0.9.12
19
+ Requires-Dist: reflex>=0.10.0
20
+ Provides-Extra: timeline
21
+ Requires-Dist: reflex-knightlab-timeline>=0.1.2; extra == "timeline"
20
22
  Provides-Extra: dev
21
23
  Requires-Dist: build; extra == "dev"
22
24
  Requires-Dist: twine; extra == "dev"
23
25
  Requires-Dist: pytest; extra == "dev"
24
26
  Requires-Dist: ruff; extra == "dev"
27
+ Requires-Dist: reflex-knightlab-timeline>=0.1.2; extra == "dev"
25
28
  Dynamic: license-file
26
29
 
27
30
  # reflex-silverpoint-react
@@ -39,6 +42,30 @@ components** (buttons, inputs, tabs, cards, alerts…), the `SilverpointProvider
39
42
  (`silverpoint` and `cyanotype`), the interaction events, custom readouts and the imperative handle
40
43
  (SVG export and geometry) as Python.
41
44
 
45
+ On top of that it adds, in Reflex: **palettes** for every chart and component, **20 composed
46
+ components** built on silverpoint's own frames and tones (tables, overlays, toasts…), and the
47
+ **Knight Lab timeline** drawn in silverpoint.
48
+
49
+ ### New in 0.7
50
+
51
+ - **Reflex 0.10.0**, and therefore **Python 3.11 or later** (Python 3.10 is no longer supported).
52
+ Reflex 0.10 removed `reflex component build`: stubs are regenerated with `scripts/make_stubs.py`.
53
+ - **Palettes** — `palette=` on every chart, UI component, dashboard and composed component,
54
+ `silverpoint_palette(...)` for a whole section, six presets named after drawing media
55
+ (`silverpoint`, `sanguine`, `sepia`, `iron_gall`, `verdigris`, `indigo`) and `check_palette()`
56
+ against the contrast thresholds of the data model. See [Palettes](#palettes).
57
+ - **Composed components, level A** (native markup): `sp_table` (+ `sort_rows`), `sp_pagination`,
58
+ `sp_breadcrumb`, `sp_avatar` (+ `sp_avatar_group`), `sp_statistic`, `sp_descriptions`,
59
+ `sp_timeline_list`, `sp_collapse` (+ `sp_collapse_item`), `sp_empty`, `sp_result`, `sp_textarea`
60
+ and `sp_input_number`.
61
+ - **Composed components, level B** (overlays on Radix primitives): `sp_modal`, `sp_drawer`,
62
+ `sp_popover`, `sp_tooltip`, `sp_select`, `sp_dropdown_menu` and `sp_toast` (+ `sp_toaster`), with
63
+ `sp_trigger` as their drawn button. See [Composed components](#composed-components).
64
+ - **`sp_timeline`** — Knight Lab TimelineJS drawn in silverpoint: a skin on the ground's colours and
65
+ type, plus the ground's own hand-inked frames and tone tiles on its markers, eras and buttons.
66
+ Optional extra: `pip install "reflex-silverpoint-react[timeline]"`. See [Timeline](#timeline).
67
+ - `sp_surface`, `sp_glyph`: the building blocks, for components of your own.
68
+
42
69
  ### New in 0.6
43
70
 
44
71
  - **27 new charts**, the second catalog of silverpoint 0.6:
@@ -87,7 +114,7 @@ stylesheets it needs (`@silverpoint/grounds/styles.css` and the self-hosted EB G
87
114
  `@silverpoint/fonts/fonts.css`); the UI components also import `@silverpoint/grounds/ui.css`. No
88
115
  Tailwind, no CDN.
89
116
 
90
- Requires Reflex 0.9.12 or later (React 19).
117
+ Requires Reflex 0.10.0 or later (React 19) and Python 3.11 or later.
91
118
 
92
119
  ## Quickstart
93
120
 
@@ -350,6 +377,132 @@ rx.el.form(
350
377
  - Text inputs controlled from state make a round trip per keystroke; for long text, prefer
351
378
  `default_value` and read the value on submit.
352
379
 
380
+ ## Palettes
381
+
382
+ silverpoint never writes a colour into its drawing: every stroke and fill is painted by CSS through
383
+ the ground's `--sp-` custom properties. A palette sets them, so it re-colours without re-rendering.
384
+
385
+ ```python
386
+ from reflex_silverpoint_react import PALETTES, Palette, bar_chart, check_palette, silverpoint_palette, sp_button
387
+
388
+ bar_chart(title="Orders", palette="sanguine") # a preset
389
+ sp_button("Save", variant="primary", palette={"ink": "#2C6355"}) # a dict of fields
390
+ bar_chart(title="Orders", palette=State.palette) # a state var holding a dict
391
+
392
+ MINE = Palette(substrate="#F2E8DC", ink="#8A3A24", ink_secondary="#6A4A3A", text="#4A2216")
393
+ assert not check_palette(MINE) # [] when every field meets its contrast threshold
394
+
395
+ silverpoint_palette(dashboard(...), sp_card(...), palette="indigo") # a whole section
396
+ ```
397
+
398
+ Fields: `substrate`, `ink`, `ink_secondary`, `heighten`, `rule`, `grid`, `text`, `text_muted` and
399
+ `focus` (the UI focus ring). Unset fields keep the ground's own. An element's own `palette` always
400
+ wins over its section's. A palette is **monochrome by design**: tone is built from hatch density
401
+ (Art. 6), so a chart has two inks (`ink` for the main series, `ink_secondary` for the second), never
402
+ a colour per series. `precision` mode and the system's high-contrast modes still apply on top.
403
+ `silverpoint_provider` takes no palette (it renders no element): use `silverpoint_palette`.
404
+
405
+ ## Composed components
406
+
407
+ Components built in Reflex on silverpoint's markup contract: `@silverpoint/grounds/ui.css` draws its
408
+ hand-inked frames, hatched tones and heightening on any element that follows it, so these are drawn
409
+ with exactly the same pieces as the `Sp` components. They are **Reflex-only** (silverpoint's React,
410
+ Vue and Angular adapters do not have them) and, unlike the `Sp` components, they do not read
411
+ `silverpoint_provider`: pass `ground`, `substrate`, `mode` and `palette` explicitly.
412
+
413
+ ```python
414
+ from reflex_silverpoint_react import sort_rows, sp_pagination, sp_table
415
+
416
+ sp_table(
417
+ [
418
+ {"key": "team", "title": "Team", "sortable": True},
419
+ {"key": "load", "title": "Load", "align": "end", "sortable": True},
420
+ ],
421
+ State.rows,
422
+ caption="On-call teams",
423
+ sort_key=State.sort_key,
424
+ sort_order=State.sort_order,
425
+ on_sort=State.sort, # receives the key; sort with sort_rows(rows, key, order)
426
+ striped=True,
427
+ )
428
+ sp_pagination(total=240, current=State.page, on_change=State.set_page, show_total=True)
429
+ ```
430
+
431
+ | Level A (native) | |
432
+ |---|---|
433
+ | `sp_table` | Card-framed table: small-caps header, tabular figures, hatched striping, sortable headers with `aria-sort` |
434
+ | `sp_pagination` | Previous/next, first/last, a window around the current page (heightened); `on_change(page)` |
435
+ | `sp_breadcrumb` | `<nav>` of links; the last item is the current page |
436
+ | `sp_avatar`, `sp_avatar_group` | Portrait or initials in a drawn round (or square) frame |
437
+ | `sp_statistic` | Small-caps title, large tabular figure, prefix/suffix, trend arrow |
438
+ | `sp_descriptions` | Label–value grid (`<dl>`), optionally card-framed |
439
+ | `sp_timeline_list` | Vertical list: `done` (hatched), `current` (heightened), `pending`, `error` |
440
+ | `sp_collapse`, `sp_collapse_item` | Native `<details>`; `accordion=True` keeps one open |
441
+ | `sp_empty`, `sp_result` | Empty state; outcome with a drawn round mark |
442
+ | `sp_textarea`, `sp_input_number` | On the `SpInput` contract; `on_change` receives the value (text or number) |
443
+
444
+ | Level B (Radix primitives) | |
445
+ |---|---|
446
+ | `sp_modal`, `sp_drawer` | Dialog on a card frame over a hatched backdrop; drawer from any edge |
447
+ | `sp_popover`, `sp_tooltip` | Anchored content; tooltip on hover or keyboard focus |
448
+ | `sp_select` | Single choice with typeahead; the chosen option ticked, the highlighted one heightened |
449
+ | `sp_dropdown_menu` | Commands with shortcuts, separators and labels; `on_select(key)` |
450
+ | `sp_toast`, `sp_toaster` | Notices that stack in a corner; `error`/`warning` are announced assertively |
451
+
452
+ Behaviour (focus trapping, keyboard, dismissal, positioning, ARIA) is Radix's, at the versions
453
+ Reflex already installs. Overlays render in a portal at the end of `<body>`, outside any
454
+ `silverpoint_palette` section, so give them `palette` directly. A string `trigger` becomes
455
+ `sp_trigger(...)`, the drawn button that can forward Radix's props (`sp_button` cannot).
456
+
457
+ ```python
458
+ sp_modal(
459
+ rx.el.p("Apply the new ground?"),
460
+ trigger="Open",
461
+ title="Apply changes",
462
+ footer=sp_trigger("Apply", variant="primary", on_click=State.confirm),
463
+ open=State.modal_open,
464
+ on_open_change=State.set_modal_open,
465
+ )
466
+ sp_select(["North", "East", "South", "West"], value=State.region, on_change=State.set_region)
467
+ sp_toaster(sp_toast("Saved", State.text, kind="success", open=State.toast_open, on_open_change=State.set_toast_open))
468
+ ```
469
+
470
+ ![Composed components](docs/components.png)
471
+
472
+ ## Timeline
473
+
474
+ ```bash
475
+ pip install "reflex-silverpoint-react[timeline]" # adds reflex-knightlab-timeline
476
+ ```
477
+
478
+ ```python
479
+ from reflex_silverpoint_react import sp_timeline
480
+
481
+ sp_timeline(
482
+ DATA, # TimelineJS data: title, events, eras (or a TimelineData)
483
+ height=560,
484
+ ground="silverpoint",
485
+ substrate="cream",
486
+ mode="ink",
487
+ palette="sepia",
488
+ inked_frames=True, # the ground's hand-drawn frames and tone tiles
489
+ on_change=State.on_change, # the unique_id of the slide shown
490
+ )
491
+ ```
492
+
493
+ It works in two layers. The **skin** replaces TimelineJS's colours, type and rules with the
494
+ ground's `--sp-` properties: substrate, inks, EB Garamond, hairline rules, eras hatched by density,
495
+ the current marker heightened. The **hand** reads the ground's own frame and tone masks from hidden
496
+ probes and lays them on the markers, eras and menubar, so they are drawn with the same pieces as
497
+ the `Sp` components; `inked_frames=False` keeps the skin only, and `precision`, `cyanotype` and
498
+ forced colours fall back to exact rules by themselves. Note that TimelineJS requests its icon font
499
+ from a relative path (`/css/icons/tl-icons.*`); the Reflex dev server answers it with HTML, so the
500
+ browser logs font-decoding warnings. They come from TimelineJS, not from this package.
501
+
502
+ ![The Knight Lab timeline in silverpoint](docs/timeline.png)
503
+
504
+ ![The palettes page](docs/palettes.png)
505
+
353
506
  ## Interaction
354
507
 
355
508
  ```python
@@ -411,7 +564,7 @@ The exported SVG carries its own ground colours, so it renders the same outside
411
564
  ## Demo app
412
565
 
413
566
  ```bash
414
- uv venv && uv pip install -e .
567
+ uv venv && uv pip install -e ".[timeline]"
415
568
  cd silverpoint_react_demo
416
569
  uv run reflex run
417
570
  ```
@@ -419,24 +572,45 @@ uv run reflex run
419
572
  Pages: **Start** (quickstart, provider, ink vs precision), **Gallery** (the 60 charts, ground
420
573
  controls, demo datasets vs data from Python state with a re-roll), **Playground** (one chart, every
421
574
  rendering choice and the Python that reproduces it), **Interaction** (events, custom readout, a
422
- volvelle that turns on click, a treemap and a sunburst that drill down together, live KPI/gauge/meter, SVG and geometry export), **Dashboard** (a
575
+ volvelle that turns on click, a treemap and a sunburst that drill down together, live
576
+ KPI/gauge/meter, SVG and geometry export), **Dashboard** (a
423
577
  linked operations dashboard fed from Python state with ground, substrate, mode and column controls, a
424
578
  KPI strip, and `dashboard_link` on its own), **UI components** (`/ui`: the components controlled from
425
579
  Python state with a native form submit, and the three reference panels of the upstream example apps
426
580
  in silverpoint ink, precision and cyanotype), one **reference page per chart** (`/chart/<slug>`) and
427
- one per UI component (`/ui/<slug>`: every declared state on both grounds, usage and props). The Gallery and Playground also get a ground control, for `cyanotype`.
581
+ one per UI component (`/ui/<slug>`: every declared state on both grounds, usage and props). The
582
+ Gallery and Playground also get a ground control, for `cyanotype`, and a palette control.
583
+ New in 0.7: **More components** (`/components`: the 20 composed components wired to Python, with
584
+ ground, substrate, mode and palette controls and an event log), **Palettes** (`/palettes`: the
585
+ presets, a palette builder with colour pickers and `check_palette`, a coloured section) and
586
+ **Timeline** (`/timeline`: the Knight Lab timeline on every ground, mode and palette).
428
587
 
429
588
  ![Interaction page](docs/interaction.png)
430
589
 
431
590
  ## Development
432
591
 
433
592
  ```bash
434
- uv venv && uv pip install -e ".[dev]"
593
+ uv sync --extra dev
435
594
  uv run pytest
436
595
  uv run ruff check .
437
- uv run reflex component build # .pyi stubs + sdist and wheel in dist/
596
+ # Reflex 0.10 removed `reflex component build`: regenerate the .pyi stubs with the generator
597
+ # Reflex itself uses (plus this package's fixes), then build the sdist and wheel.
598
+ uv run python scripts/make_stubs.py
599
+ uv build
438
600
  ```
439
601
 
602
+ Before committing, run the hooks of [`.pre-commit-config.yaml`](.pre-commit-config.yaml) (ruff check
603
+ and format — including the Python blocks of this README — file hygiene and `uv lock --check`):
604
+
605
+ ```bash
606
+ pre-commit install
607
+ pre-commit run --all-files
608
+ ```
609
+
610
+ Changes per version are in [CHANGELOG.md](CHANGELOG.md); the guide for coding agents is
611
+ [AGENTS.md](AGENTS.md). Releases are published to PyPI by pushing a `vX.Y.Z` tag on `main` that
612
+ matches `version` in `pyproject.toml`.
613
+
440
614
  ## License
441
615
 
442
616
  MIT. silverpoint is MIT; the EB Garamond typeface is under the SIL Open Font License.
@@ -1,29 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: reflex-silverpoint-react
3
- Version: 0.6.0
4
- Summary: The 60 silverpoint charts, the linked Dashboard and the 17 UI components (Renaissance silverpoint drawing style) for Reflex, wrapping @silverpoint/react
5
- Author-email: Ernesto Crespo <ecrespo@gmail.com>
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/ecrespo/reflex-silverpoint-react
8
- Project-URL: Source, https://github.com/ecrespo/reflex-silverpoint-react
9
- Project-URL: Issues, https://github.com/ecrespo/reflex-silverpoint-react/issues
10
- Project-URL: Upstream, https://github.com/ecrespo/silverpoint
11
- Keywords: reflex,reflex-custom-components,charts,silverpoint,data-visualization,svg,ui-components
12
- Classifier: Development Status :: 4 - Beta
13
- Classifier: License :: OSI Approved :: MIT License
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Topic :: Scientific/Engineering :: Visualization
16
- Requires-Python: >=3.10
17
- Description-Content-Type: text/markdown
18
- License-File: LICENSE
19
- Requires-Dist: reflex>=0.9.12
20
- Provides-Extra: dev
21
- Requires-Dist: build; extra == "dev"
22
- Requires-Dist: twine; extra == "dev"
23
- Requires-Dist: pytest; extra == "dev"
24
- Requires-Dist: ruff; extra == "dev"
25
- Dynamic: license-file
26
-
27
1
  # reflex-silverpoint-react
28
2
 
29
3
  A [Reflex](https://reflex.dev) custom component for **[silverpoint](https://github.com/ecrespo/silverpoint)**:
@@ -39,6 +13,30 @@ components** (buttons, inputs, tabs, cards, alerts…), the `SilverpointProvider
39
13
  (`silverpoint` and `cyanotype`), the interaction events, custom readouts and the imperative handle
40
14
  (SVG export and geometry) as Python.
41
15
 
16
+ On top of that it adds, in Reflex: **palettes** for every chart and component, **20 composed
17
+ components** built on silverpoint's own frames and tones (tables, overlays, toasts…), and the
18
+ **Knight Lab timeline** drawn in silverpoint.
19
+
20
+ ### New in 0.7
21
+
22
+ - **Reflex 0.10.0**, and therefore **Python 3.11 or later** (Python 3.10 is no longer supported).
23
+ Reflex 0.10 removed `reflex component build`: stubs are regenerated with `scripts/make_stubs.py`.
24
+ - **Palettes** — `palette=` on every chart, UI component, dashboard and composed component,
25
+ `silverpoint_palette(...)` for a whole section, six presets named after drawing media
26
+ (`silverpoint`, `sanguine`, `sepia`, `iron_gall`, `verdigris`, `indigo`) and `check_palette()`
27
+ against the contrast thresholds of the data model. See [Palettes](#palettes).
28
+ - **Composed components, level A** (native markup): `sp_table` (+ `sort_rows`), `sp_pagination`,
29
+ `sp_breadcrumb`, `sp_avatar` (+ `sp_avatar_group`), `sp_statistic`, `sp_descriptions`,
30
+ `sp_timeline_list`, `sp_collapse` (+ `sp_collapse_item`), `sp_empty`, `sp_result`, `sp_textarea`
31
+ and `sp_input_number`.
32
+ - **Composed components, level B** (overlays on Radix primitives): `sp_modal`, `sp_drawer`,
33
+ `sp_popover`, `sp_tooltip`, `sp_select`, `sp_dropdown_menu` and `sp_toast` (+ `sp_toaster`), with
34
+ `sp_trigger` as their drawn button. See [Composed components](#composed-components).
35
+ - **`sp_timeline`** — Knight Lab TimelineJS drawn in silverpoint: a skin on the ground's colours and
36
+ type, plus the ground's own hand-inked frames and tone tiles on its markers, eras and buttons.
37
+ Optional extra: `pip install "reflex-silverpoint-react[timeline]"`. See [Timeline](#timeline).
38
+ - `sp_surface`, `sp_glyph`: the building blocks, for components of your own.
39
+
42
40
  ### New in 0.6
43
41
 
44
42
  - **27 new charts**, the second catalog of silverpoint 0.6:
@@ -87,7 +85,7 @@ stylesheets it needs (`@silverpoint/grounds/styles.css` and the self-hosted EB G
87
85
  `@silverpoint/fonts/fonts.css`); the UI components also import `@silverpoint/grounds/ui.css`. No
88
86
  Tailwind, no CDN.
89
87
 
90
- Requires Reflex 0.9.12 or later (React 19).
88
+ Requires Reflex 0.10.0 or later (React 19) and Python 3.11 or later.
91
89
 
92
90
  ## Quickstart
93
91
 
@@ -350,6 +348,132 @@ rx.el.form(
350
348
  - Text inputs controlled from state make a round trip per keystroke; for long text, prefer
351
349
  `default_value` and read the value on submit.
352
350
 
351
+ ## Palettes
352
+
353
+ silverpoint never writes a colour into its drawing: every stroke and fill is painted by CSS through
354
+ the ground's `--sp-` custom properties. A palette sets them, so it re-colours without re-rendering.
355
+
356
+ ```python
357
+ from reflex_silverpoint_react import PALETTES, Palette, bar_chart, check_palette, silverpoint_palette, sp_button
358
+
359
+ bar_chart(title="Orders", palette="sanguine") # a preset
360
+ sp_button("Save", variant="primary", palette={"ink": "#2C6355"}) # a dict of fields
361
+ bar_chart(title="Orders", palette=State.palette) # a state var holding a dict
362
+
363
+ MINE = Palette(substrate="#F2E8DC", ink="#8A3A24", ink_secondary="#6A4A3A", text="#4A2216")
364
+ assert not check_palette(MINE) # [] when every field meets its contrast threshold
365
+
366
+ silverpoint_palette(dashboard(...), sp_card(...), palette="indigo") # a whole section
367
+ ```
368
+
369
+ Fields: `substrate`, `ink`, `ink_secondary`, `heighten`, `rule`, `grid`, `text`, `text_muted` and
370
+ `focus` (the UI focus ring). Unset fields keep the ground's own. An element's own `palette` always
371
+ wins over its section's. A palette is **monochrome by design**: tone is built from hatch density
372
+ (Art. 6), so a chart has two inks (`ink` for the main series, `ink_secondary` for the second), never
373
+ a colour per series. `precision` mode and the system's high-contrast modes still apply on top.
374
+ `silverpoint_provider` takes no palette (it renders no element): use `silverpoint_palette`.
375
+
376
+ ## Composed components
377
+
378
+ Components built in Reflex on silverpoint's markup contract: `@silverpoint/grounds/ui.css` draws its
379
+ hand-inked frames, hatched tones and heightening on any element that follows it, so these are drawn
380
+ with exactly the same pieces as the `Sp` components. They are **Reflex-only** (silverpoint's React,
381
+ Vue and Angular adapters do not have them) and, unlike the `Sp` components, they do not read
382
+ `silverpoint_provider`: pass `ground`, `substrate`, `mode` and `palette` explicitly.
383
+
384
+ ```python
385
+ from reflex_silverpoint_react import sort_rows, sp_pagination, sp_table
386
+
387
+ sp_table(
388
+ [
389
+ {"key": "team", "title": "Team", "sortable": True},
390
+ {"key": "load", "title": "Load", "align": "end", "sortable": True},
391
+ ],
392
+ State.rows,
393
+ caption="On-call teams",
394
+ sort_key=State.sort_key,
395
+ sort_order=State.sort_order,
396
+ on_sort=State.sort, # receives the key; sort with sort_rows(rows, key, order)
397
+ striped=True,
398
+ )
399
+ sp_pagination(total=240, current=State.page, on_change=State.set_page, show_total=True)
400
+ ```
401
+
402
+ | Level A (native) | |
403
+ |---|---|
404
+ | `sp_table` | Card-framed table: small-caps header, tabular figures, hatched striping, sortable headers with `aria-sort` |
405
+ | `sp_pagination` | Previous/next, first/last, a window around the current page (heightened); `on_change(page)` |
406
+ | `sp_breadcrumb` | `<nav>` of links; the last item is the current page |
407
+ | `sp_avatar`, `sp_avatar_group` | Portrait or initials in a drawn round (or square) frame |
408
+ | `sp_statistic` | Small-caps title, large tabular figure, prefix/suffix, trend arrow |
409
+ | `sp_descriptions` | Label–value grid (`<dl>`), optionally card-framed |
410
+ | `sp_timeline_list` | Vertical list: `done` (hatched), `current` (heightened), `pending`, `error` |
411
+ | `sp_collapse`, `sp_collapse_item` | Native `<details>`; `accordion=True` keeps one open |
412
+ | `sp_empty`, `sp_result` | Empty state; outcome with a drawn round mark |
413
+ | `sp_textarea`, `sp_input_number` | On the `SpInput` contract; `on_change` receives the value (text or number) |
414
+
415
+ | Level B (Radix primitives) | |
416
+ |---|---|
417
+ | `sp_modal`, `sp_drawer` | Dialog on a card frame over a hatched backdrop; drawer from any edge |
418
+ | `sp_popover`, `sp_tooltip` | Anchored content; tooltip on hover or keyboard focus |
419
+ | `sp_select` | Single choice with typeahead; the chosen option ticked, the highlighted one heightened |
420
+ | `sp_dropdown_menu` | Commands with shortcuts, separators and labels; `on_select(key)` |
421
+ | `sp_toast`, `sp_toaster` | Notices that stack in a corner; `error`/`warning` are announced assertively |
422
+
423
+ Behaviour (focus trapping, keyboard, dismissal, positioning, ARIA) is Radix's, at the versions
424
+ Reflex already installs. Overlays render in a portal at the end of `<body>`, outside any
425
+ `silverpoint_palette` section, so give them `palette` directly. A string `trigger` becomes
426
+ `sp_trigger(...)`, the drawn button that can forward Radix's props (`sp_button` cannot).
427
+
428
+ ```python
429
+ sp_modal(
430
+ rx.el.p("Apply the new ground?"),
431
+ trigger="Open",
432
+ title="Apply changes",
433
+ footer=sp_trigger("Apply", variant="primary", on_click=State.confirm),
434
+ open=State.modal_open,
435
+ on_open_change=State.set_modal_open,
436
+ )
437
+ sp_select(["North", "East", "South", "West"], value=State.region, on_change=State.set_region)
438
+ sp_toaster(sp_toast("Saved", State.text, kind="success", open=State.toast_open, on_open_change=State.set_toast_open))
439
+ ```
440
+
441
+ ![Composed components](docs/components.png)
442
+
443
+ ## Timeline
444
+
445
+ ```bash
446
+ pip install "reflex-silverpoint-react[timeline]" # adds reflex-knightlab-timeline
447
+ ```
448
+
449
+ ```python
450
+ from reflex_silverpoint_react import sp_timeline
451
+
452
+ sp_timeline(
453
+ DATA, # TimelineJS data: title, events, eras (or a TimelineData)
454
+ height=560,
455
+ ground="silverpoint",
456
+ substrate="cream",
457
+ mode="ink",
458
+ palette="sepia",
459
+ inked_frames=True, # the ground's hand-drawn frames and tone tiles
460
+ on_change=State.on_change, # the unique_id of the slide shown
461
+ )
462
+ ```
463
+
464
+ It works in two layers. The **skin** replaces TimelineJS's colours, type and rules with the
465
+ ground's `--sp-` properties: substrate, inks, EB Garamond, hairline rules, eras hatched by density,
466
+ the current marker heightened. The **hand** reads the ground's own frame and tone masks from hidden
467
+ probes and lays them on the markers, eras and menubar, so they are drawn with the same pieces as
468
+ the `Sp` components; `inked_frames=False` keeps the skin only, and `precision`, `cyanotype` and
469
+ forced colours fall back to exact rules by themselves. Note that TimelineJS requests its icon font
470
+ from a relative path (`/css/icons/tl-icons.*`); the Reflex dev server answers it with HTML, so the
471
+ browser logs font-decoding warnings. They come from TimelineJS, not from this package.
472
+
473
+ ![The Knight Lab timeline in silverpoint](docs/timeline.png)
474
+
475
+ ![The palettes page](docs/palettes.png)
476
+
353
477
  ## Interaction
354
478
 
355
479
  ```python
@@ -411,7 +535,7 @@ The exported SVG carries its own ground colours, so it renders the same outside
411
535
  ## Demo app
412
536
 
413
537
  ```bash
414
- uv venv && uv pip install -e .
538
+ uv venv && uv pip install -e ".[timeline]"
415
539
  cd silverpoint_react_demo
416
540
  uv run reflex run
417
541
  ```
@@ -419,24 +543,45 @@ uv run reflex run
419
543
  Pages: **Start** (quickstart, provider, ink vs precision), **Gallery** (the 60 charts, ground
420
544
  controls, demo datasets vs data from Python state with a re-roll), **Playground** (one chart, every
421
545
  rendering choice and the Python that reproduces it), **Interaction** (events, custom readout, a
422
- volvelle that turns on click, a treemap and a sunburst that drill down together, live KPI/gauge/meter, SVG and geometry export), **Dashboard** (a
546
+ volvelle that turns on click, a treemap and a sunburst that drill down together, live
547
+ KPI/gauge/meter, SVG and geometry export), **Dashboard** (a
423
548
  linked operations dashboard fed from Python state with ground, substrate, mode and column controls, a
424
549
  KPI strip, and `dashboard_link` on its own), **UI components** (`/ui`: the components controlled from
425
550
  Python state with a native form submit, and the three reference panels of the upstream example apps
426
551
  in silverpoint ink, precision and cyanotype), one **reference page per chart** (`/chart/<slug>`) and
427
- one per UI component (`/ui/<slug>`: every declared state on both grounds, usage and props). The Gallery and Playground also get a ground control, for `cyanotype`.
552
+ one per UI component (`/ui/<slug>`: every declared state on both grounds, usage and props). The
553
+ Gallery and Playground also get a ground control, for `cyanotype`, and a palette control.
554
+ New in 0.7: **More components** (`/components`: the 20 composed components wired to Python, with
555
+ ground, substrate, mode and palette controls and an event log), **Palettes** (`/palettes`: the
556
+ presets, a palette builder with colour pickers and `check_palette`, a coloured section) and
557
+ **Timeline** (`/timeline`: the Knight Lab timeline on every ground, mode and palette).
428
558
 
429
559
  ![Interaction page](docs/interaction.png)
430
560
 
431
561
  ## Development
432
562
 
433
563
  ```bash
434
- uv venv && uv pip install -e ".[dev]"
564
+ uv sync --extra dev
435
565
  uv run pytest
436
566
  uv run ruff check .
437
- uv run reflex component build # .pyi stubs + sdist and wheel in dist/
567
+ # Reflex 0.10 removed `reflex component build`: regenerate the .pyi stubs with the generator
568
+ # Reflex itself uses (plus this package's fixes), then build the sdist and wheel.
569
+ uv run python scripts/make_stubs.py
570
+ uv build
438
571
  ```
439
572
 
573
+ Before committing, run the hooks of [`.pre-commit-config.yaml`](.pre-commit-config.yaml) (ruff check
574
+ and format — including the Python blocks of this README — file hygiene and `uv lock --check`):
575
+
576
+ ```bash
577
+ pre-commit install
578
+ pre-commit run --all-files
579
+ ```
580
+
581
+ Changes per version are in [CHANGELOG.md](CHANGELOG.md); the guide for coding agents is
582
+ [AGENTS.md](AGENTS.md). Releases are published to PyPI by pushing a `vX.Y.Z` tag on `main` that
583
+ matches `version` in `pyproject.toml`.
584
+
440
585
  ## License
441
586
 
442
587
  MIT. silverpoint is MIT; the EB Garamond typeface is under the SIL Open Font License.