@dstackai/sqircle 0.1.0 → 0.1.2

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.
@@ -6,13 +6,14 @@ Read these files when changing how a squircle looks. They are visual contracts s
6
6
  | --- | --- |
7
7
  | [geometry.md](./geometry.md) | Superellipse sampling, projection, side-wall visibility, layer offsets, regeneration checklist |
8
8
  | [rendering.md](./rendering.md) | Gradients, edge sharpness, draw order, dashed inlay, text path and paint rules |
9
- | [colors.md](./colors.md) | Palette stops, text contrast colors, edge colors, hover palette rules |
9
+ | [colors.md](./colors.md) | Alpha palettes, text contrast colors, edge colors, hover palette rules |
10
10
  | [single-squircle-states.md](./single-squircle-states.md) | Reusable one-squircle state recipes for solid, transparent, wireframe, text, dash, and text + dash |
11
11
 
12
12
  ## Design Source Of Truth
13
13
 
14
14
  - Geometry is generated from math, not hand-edited point lists.
15
15
  - Filled faces use user-space gradients plus tiny in-family edge strokes.
16
+ - Solid faces can use `off`, `fluid`, or `frosted` top-surface effects without moving geometry.
16
17
  - Wireframe faces use gradient strokes with matching top and bottom curves.
17
18
  - React top-plane text is one live SVG text element in every mode. Static fixtures keep a single compound `GPU` path for their fixed example.
18
19
  - Solid/transparent annotations use contrast colors; wireframe annotations use wire gradients.
@@ -4,15 +4,11 @@ This file is the palette contract for variants, gradients, text labels, and edge
4
4
 
5
5
  ## Sources Of Truth
6
6
 
7
- Colors are defined in shared CSS and in each self-contained HTML page that embeds SVG definitions. Keep them synchronized:
7
+ Colors are defined in `src/squircle/palettes.ts` and consumed by `SquircleScene`. The root HTML files are Vite shells and do not own gradient stops. Keep these synchronized:
8
8
 
9
- 1. SVG gradients in `index.html`, `demo.html`, and `constructor.html`:
10
- - `top-13..top-20`
11
- - `gpu-surface-13..gpu-surface-20` in static fixtures, mirrored as `text-surface-*` in React output
12
- - `side-13..side-20`
13
- 2. CSS variables in `public/static/styles.css`:
14
- - Selected-variant selectors for `.hero-card` and `.single-drawer`
15
- - `.v13..v20` classes for variant cards
9
+ 1. `SQUIRCLE_PALETTES`: alpha palettes `13..20`.
10
+ 2. Renderer docs in `README.md` and `docs/react/README.md`.
11
+ 3. This design file.
16
12
 
17
13
  Do not add or rename a variant in only one place.
18
14
 
@@ -45,7 +41,7 @@ The text surface gradient repeats the matching `top-*` stops with label-local co
45
41
 
46
42
  ## CSS Variable Contract
47
43
 
48
- Every selected-variant selector must target both `.hero-card` and `.single-drawer`:
44
+ Legacy static snapshots used CSS variables such as `--top-fill` and `--side-fill`. New React work should not edit those snapshots as source of truth. If a legacy static snapshot must be refreshed, keep every selected-variant selector targeting both `.hero-card` and `.single-drawer`:
49
45
 
50
46
  ```css
51
47
  #variant-15:checked ~ .hero-card,
@@ -76,41 +72,41 @@ Variant classes may also define fallback ghost colors, but those must remain vis
76
72
 
77
73
  ## Hover Palettes
78
74
 
79
- `constructor.html` and `demo.html` can use any palette id as a layer's `hoverPalette`. The base variant keeps `.v${palette}` or inherits the selected page variant, and the hover variant receives `.v${hoverPalette}`. This means hover color changes use the same documented gradients, label contrast, and wireframe stroke colors as normal rendering.
75
+ React examples and generated code can use any palette id as a layer's hover `paletteId`. The base variant keeps its normal palette, and the hover variant receives its own `paletteId`. This means hover color changes use the same documented gradients, label contrast, and wireframe stroke colors as normal rendering.
80
76
 
81
- Do not implement hover color with filters, opacity hacks, or untracked ad hoc colors. Hover color is a normal palette swap. `hoverPalette` may be the same id as the base `palette`; that is useful when hover should change only material while preserving color. If both hover state and hover palette match the base state and palette, the result is a deliberate no-op and should still be exported exactly, but it should not render or crossfade a hover variant.
77
+ Do not implement hover color with filters, opacity hacks, or untracked ad hoc colors. Hover color is a normal palette swap. Hover `paletteId` may be the same id as the base `paletteId`; that is useful when hover should change only material while preserving color. If the resolved hover variant matches the base variant, `SquircleScene` should not render or crossfade a hover copy.
82
78
 
83
79
  ## Annotation Auto And Overrides
84
80
 
85
- Static pages and constructor layers with `gpuColor: "contrast"` and `dashColor: "contrast"` use the palette's automatic annotation colors. React configs use `textColor: "contrast"` for the same concept. Solid and transparent top-plane annotations use `--label-fill`. This applies to both the filled text path and the solid dashed `#top-inlay`, so default text + dash states keep one automatic contrast color.
81
+ React layers with `textColor: "contrast"` and `dashColor: "contrast"` use the palette's automatic annotation colors. Solid and transparent top-plane annotations use the palette `labelFill`. This applies to both filled text and solid dashed inlays, so default text + dash states keep one automatic contrast color.
86
82
 
87
83
  - Darker top gradients may use a near-white label. Current example: `15 Alpha` uses `#f7fbff`.
88
84
  - Lighter top gradients should use dark in-family annotation colors.
89
85
  - Do not add a second outline copy for automatic contrast.
90
86
 
91
- Wireframe text labels ignore `--label-fill` and use gradient wire paint:
87
+ Wireframe text labels ignore fixed label paint and use gradient wire paint:
92
88
 
93
89
  ```css
94
90
  fill: none !important;
95
- stroke: var(--top-fill);
91
+ stroke: url("#...text-wire...");
96
92
  stroke-width: var(--label-wire-width);
97
93
  ```
98
94
 
99
- `#top-inlay-wire` also uses `stroke: var(--top-fill)`.
95
+ Wireframe inlays use the top-face gradient.
100
96
 
101
- In React, `textStyle: "wireframe"` outlines the same live SVG text element used by filled mode. On solid/transparent material, outline paint comes from `textColor`; `contrast` resolves to `--label-fill`, matching filled text and solid dash. On wireframe material, outline paint comes from label-local `textWire` gradients; do not sample the full-face `--top-fill` ramp there, and do not replace the text with single-stroke lettering. The static constructor keeps `gpuStyle`, `gpuColor`, and `#gpu-wire-*` names only for its GPU example schema.
97
+ In React, `textStyle: "wireframe"` outlines the same live SVG text element used by filled mode. On solid/transparent material, outline paint comes from `textColor`; `contrast` resolves to `labelFill`, matching filled text and solid dash. On wireframe material, outline paint comes from label-local `textWire` gradients; do not sample the full-face top ramp there, and do not replace the text with single-stroke lettering.
102
98
 
103
- `constructor.html` adds explicit annotation overrides:
99
+ `SquircleVariantConfig` supports explicit annotation overrides:
104
100
 
105
101
  | Field | Values | Paint |
106
102
  | --- | --- | --- |
107
- | `gpuColor` | `contrast`, `white`, `black` | Filled or outlined GPU paint for solid/transparent material; `contrast` appears as `Auto` in the UI |
108
- | `gpuStyle` | `solid`, `wireframe` | Filled or outlined GPU label |
103
+ | `textColor` | `contrast`, `white`, `black` | Filled or outlined text paint for solid/transparent material; `contrast` appears as `Auto` in the UI |
104
+ | `textStyle` | `solid`, `wireframe` | Filled or outlined text label |
109
105
  | `dashColor` | `contrast`, `white`, `black` | Dash stroke for solid/transparent material; `contrast` appears as `Auto` in the UI |
110
106
 
111
- Use `Auto` when the annotation should follow the palette. The exported value is `contrast`. Use `white` or `black` only when the user deliberately wants fixed annotation paint. React generated code emits `textColor`; the static constructor stores `gpuColor`. On wireframe material, dash always uses `--top-fill`; text uses the text surface gradient at full opacity when `textStyle` is `solid` and the text wire gradient when `textStyle` is `wireframe`. Dash symbol selection is not configurable in the constructor; it follows the squircle material.
107
+ Use `Auto` when the annotation should follow the palette. The exported value is `contrast`. Use `white` or `black` only when the user deliberately wants fixed annotation paint. On wireframe material, dash always uses the top gradient; text uses the text surface gradient at full opacity when `textStyle` is `solid` and the text wire gradient when `textStyle` is `wireframe`.
112
108
 
113
- Legacy configs with `gpuStyle: "transparent"` normalize to `solid`; there is no third text paint control.
109
+ Deprecated `gpuColor` and `gpuStyle` aliases are accepted only for older snippets. Legacy configs with `gpuStyle: "transparent"` normalize to `solid`; there is no third text paint control.
114
110
 
115
111
  ## Edge Colors
116
112
 
@@ -136,15 +132,9 @@ This is intentional: light does not interact with a wireframe surface, so top an
136
132
 
137
133
  ## Adding A Variant
138
134
 
139
- When adding a new variant:
140
-
141
- 1. Add `top-*`, `gpu-surface-*`, and `side-*` gradients in `index.html`.
142
- 2. Mirror those gradients in `demo.html` and `constructor.html` if those pages need the new palette.
143
- 3. Add a hidden variant radio.
144
- 4. Add a selected-variant CSS selector targeting `.hero-card` and `.single-drawer`.
145
- 5. Add a `.v*` class with matching variables.
146
- 6. Add the palette to the constructor `PALETTES` array.
147
- 7. Add a variant card in the drawer.
148
- 8. Add the title span in the hero copy.
149
- 9. Update this palette table.
150
- 10. Render hero, variant drawer, single-state drawer, demo presets, and constructor controls to confirm synchronized colors.
135
+ When adding a new palette:
136
+
137
+ 1. Add it to `SQUIRCLE_PALETTES`.
138
+ 2. Include top, side, text-wire, label fill, top edge, side edge, and swatch values.
139
+ 3. Update the palette table above.
140
+ 4. Render the examples and constructor controls.
@@ -89,25 +89,26 @@ Rotate the sampled array if needed so the selected front-facing indices are emit
89
89
 
90
90
  The complementary back-bottom edge is emitted as `ghost-hidden` and is only shown in wireframe mode.
91
91
 
92
- ## Generated SVG Blocks
92
+ ## Generated React Geometry
93
93
 
94
- The generator emits:
94
+ `src/squircle/geometry.ts` returns the geometry consumed by `SquircleScene`:
95
95
 
96
- - `#prism-ghost` with `ghost-hidden`, `ghost-side`, and `ghost-top`
97
- - `#prism-active` with `active-side` and `active-top`
98
- - `#top-inlay` and `#top-inlay-wire`
99
- - `#label-gpu`, documented separately in [rendering.md](./rendering.md)
96
+ - `topPoints`: lit top-face polygon.
97
+ - `wallPoints`: one continuous front side-wall polygon.
98
+ - `hiddenPoints`: back/bottom edge used only in wireframe mode.
99
+ - `inlayPoints`: top-plane dashed squircle polygon.
100
+ - `labelTransform`: `matrix(cosA, sinA, -cosA, sinA, cx, cy - h)` for live SVG text.
101
+ - `topBounds` and `sideBounds`: gradient coordinate boxes.
100
102
 
101
103
  ## Regeneration Checklist
102
104
 
103
- When changing geometry, regenerate the polygon coordinates from the formulas above and keep these in sync:
105
+ When changing geometry, update the generator and keep these in sync:
104
106
 
105
- - The generated comment in `index.html`
106
- - All SVG `viewBox` values
107
- - `.hero-squircle`, `.mini-squircle`, and `.single-squircle` aspect ratios in `public/static/styles.css`
108
- - Both generated inlay copies (`#top-inlay` and `#top-inlay-wire`)
109
- - Every `.plane-label` transform if `cosA`, `sinA`, `cx`, or `cy - h` changes
110
- - Gradient bboxes documented in [rendering.md](./rendering.md)
107
+ - `DEFAULT_GEOMETRY` in `src/squircle/geometry.ts`.
108
+ - `SquircleScene` gradient definitions in `src/squircle/SquircleScene.tsx`.
109
+ - Example dimensions in `src/pages`.
110
+ - Docs in [rendering.md](./rendering.md) and [single-squircle-states.md](./single-squircle-states.md).
111
+ - Legacy snapshots only if you intentionally refresh `legacy-static/`.
111
112
 
112
113
  For small visual changes:
113
114
 
@@ -18,6 +18,60 @@ React uses `text-surface-*` gradients to remap the top face stops into the label
18
18
 
19
19
  Wireframe mode uses the top gradient as the stroke gradient so upper and lower curves match. This intentionally avoids fake lighting on wireframes.
20
20
 
21
+ ## Solid Surface Effects
22
+
23
+ `SquircleVariantConfig.effect` changes only the top-face rendering for `material: "solid"`. It is ignored by `transparent` and `wireframe`.
24
+
25
+ | Effect | Rendering |
26
+ | --- | --- |
27
+ | `off` | A static top polygon filled by the resolved top gradient. This is the default. |
28
+ | `fluid` | A full-resolution top-face `clipPath` containing an isometrically projected base color field plus blurred moving color blobs. A second lighter blurred layer uses screen blending. No displacement map is used. |
29
+ | `frosted` | The same projected moving-blob field, muted by a deep base, then finished in screen space with a pale veil and a brighter neutral rim. |
30
+
31
+ The moving effects are interval-driven inside `SquircleScene`; they do not query the global DOM and they do not use `requestAnimationFrame`. Motion is enabled only when a visible base or hover variant resolves to a solid `fluid` or `frosted` state.
32
+
33
+ Effect color layers are authored in the flat top-face local coordinate system, not in screen coordinates. Local `(0, 0)` is the center of the raw superellipse, and local `a` is `geometry.config.halfSize`. The whole color field is then wrapped in the same isometric matrix used by labels:
34
+
35
+ ```text
36
+ matrix(cosA, sinA, -cosA, sinA, cx, cy - h)
37
+ ```
38
+
39
+ This means circles are intentionally authored as local circles, then become foreshortened ellipses on the tilted top plane. The top clip path stays in screen space around the generated `topPoints` polygon. For `frosted`, the white veil and bright rim also stay in screen space; only the blob/color field is projected.
40
+
41
+ The SVG structure for effect faces must keep this order:
42
+
43
+ ```svg
44
+ <g clip-path="url(#top-clip)">
45
+ <g transform="matrix(cosA,sinA,-cosA,sinA,cx,cy-h)">
46
+ <rect ... />
47
+ <g filter="url(#local-blur)">
48
+ <circle ... />
49
+ </g>
50
+ </g>
51
+ <!-- frost-only veil stays here in screen space -->
52
+ </g>
53
+ <!-- rim and outline stay here in screen space -->
54
+ ```
55
+
56
+ Effect scale is always derived from `W = 2 * a`, the local top-plane width, not from screen pixels or the projected bbox:
57
+
58
+ - Main blob radius is about `0.45 * W`.
59
+ - Blur is about `0.13 * W`, keeping `blur / radius` near `0.27`.
60
+ - Drift amplitude is about `0.20..0.25 * W`.
61
+ - Blob centers are allowed to sit outside `+-a` so every frame heavily overlaps and overfills the clipped face.
62
+ - The base rectangle covers `+-1.3a`, so the face never shows gaps between blobs.
63
+
64
+ The blur filters use `filterUnits="userSpaceOnUse"` and `primitiveUnits="userSpaceOnUse"` with a local filter region centered around `(0, 0)`. If `halfSize` changes, blob radii, blur, positions, base rect, and motion amplitudes must all scale from `a` or `W` together. Scaling only some of them makes the blobs collapse into visible circles.
65
+
66
+ Effect invariants:
67
+
68
+ - The clip path uses the same generated `topPoints` polygon as the normal top face.
69
+ - Fluid/frosted color blobs must never be placed directly in screen coordinates.
70
+ - Fluid/frosted must not use `feDisplacementMap`; it turns the smooth surface field into raster clouds.
71
+ - Annotations render after the effect, so text and dash stay readable and stay glued to the top plane.
72
+ - Side wall geometry, layer offsets, hover transitions, and annotation transforms do not change when the effect changes.
73
+ - Effect colors are derived from the selected alpha palette's top and side stops, except neutral white veil/rim overlays used only for glassy sharpness.
74
+
21
75
  ## Sharpness Edge
22
76
 
23
77
  Every filled face has a tiny in-family edge stroke:
@@ -42,16 +96,15 @@ The top face is drawn after the side wall, so it hides the back half of the extr
42
96
 
43
97
  The middle-layer dashed squircle is generated from the same superellipse at `0.6 * a` and projected at `z = h`, so it lies on the top plane. The static copies are:
44
98
 
45
- - `#top-inlay` for solid/transparent rendering
46
- - `#top-inlay-wire` for wireframe rendering
99
+ `SquircleScene` renders the dashed inlay as the same top-plane polygon in every material. Paint changes by material:
47
100
 
48
- By default, solid and transparent inlays use `stroke: var(--label-fill)`, the same contrast token as the filled text label. Wireframe inlays use `stroke: var(--top-fill)`, the same gradient token as the wireframe text outline.
101
+ By default, solid and transparent inlays use the palette label color, the same contrast token as filled text. Wireframe inlays use the top-face gradient, matching the prism wires.
49
102
 
50
- `constructor.html` can override dash color per layer. `dashColor: "contrast"` keeps the default token for the material's dash symbol, while `dashColor: "white"` and `dashColor: "black"` intentionally use fixed stroke colors. Dash symbol choice follows material: solid and transparent states use `#top-inlay`; wireframe states use `#top-inlay-wire`.
103
+ `dashColor: "contrast"` keeps the material default. `dashColor: "white"` and `dashColor: "black"` intentionally use fixed stroke colors on solid/transparent material. Wireframe material ignores fixed dash color and uses the wire gradient.
51
104
 
52
105
  ## Label Source
53
106
 
54
- React renders top-plane text as one live SVG `<text>` element, so component configs can pass arbitrary strings such as `text: "GPU"`, `text: "CUDA"`, or `text: "AI"`. The static fixtures still store the current `GPU` demo glyph as `#label-gpu` in `<defs>` because those pages are fixed examples.
107
+ React renders top-plane text as one live SVG `<text>` element, so component configs can pass arbitrary strings such as `text: "GPU"`, `text: "CUDA"`, or `text: "AI"`. `GPU` is only the example string used by the local demos.
55
108
 
56
109
  Current label parameters:
57
110
 
@@ -62,7 +115,7 @@ Current label parameters:
62
115
  - Centered bbox: `x = -63.0293..63.0293`, `y = -22.9473..22.9473`
63
116
  - Label wire stroke: `1.1`
64
117
 
65
- For static fixture glyphs, generate the path with `opentype.js@2.0.0`:
118
+ The old static snapshots used an outlined glyph path generated with `opentype.js@2.0.0`. Do not use that path technique in new component work; keep live text so arbitrary strings render correctly.
66
119
 
67
120
  ```js
68
121
  const font = opentype.parse(fs.readFileSync("/System/Library/Fonts/Supplemental/Arial.ttf").buffer);
@@ -72,11 +125,11 @@ const tx = -((box.x1 + box.x2) / 2);
72
125
  const ty = -((box.y1 + box.y2) / 2);
73
126
  ```
74
127
 
75
- Apply `tx` and `ty` to every path command coordinate before writing the static `d` attribute. Keep `fill-rule="evenodd"` and `clip-rule="evenodd"` on the path so counters, such as the hole inside `P`, remain true holes.
128
+ If a legacy snapshot ever needs refreshing, apply `tx` and `ty` to every path command coordinate before writing the static `d` attribute, and keep `fill-rule="evenodd"` plus `clip-rule="evenodd"` so counters remain true holes.
76
129
 
77
130
  ## Label Transform And Paint
78
131
 
79
- Every visible static label is a single `<use>` of `#label-gpu`, and every React label is a single `<text>` element. Both are transformed onto the top plane with:
132
+ Every React label is a single `<text>` element transformed onto the top plane with:
80
133
 
81
134
  ```text
82
135
  matrix(cosA, sinA, -cosA, sinA, cx, cy - h)
@@ -90,27 +143,27 @@ matrix(0.94, 0.342, -0.94, 0.342, 400, 145.6)
90
143
 
91
144
  The transform never changes between solid and wireframe modes; only paint changes. React and constructor defaults intentionally mirror the original generated GPU path: `textSize: 62`, `textFontFamily: "Arial, Helvetica, sans-serif"`, and `textFontWeight: 400`.
92
145
 
93
- Solid label style:
146
+ Solid text style:
94
147
 
95
- - `.plane-label` uses `fill: var(--label-fill)` and `stroke: none` when `textColor` is `contrast`.
96
- - `#top-inlay` uses `stroke: var(--label-fill)` so dashed inlays match the filled label color.
148
+ - The label uses palette label paint when `textColor` is `contrast`.
149
+ - Solid/transparent dash uses the same palette label paint by default.
97
150
  - `15 Alpha` uses `#f7fbff` because its top gradient is dark.
98
151
  - Lighter variants use dark in-family label fills.
99
152
 
100
- Wireframe label style:
153
+ Wireframe text style:
101
154
 
102
155
  - `textStyle: "wireframe"` uses the same live text element as filled mode.
103
- - It sets `fill:none !important` and uses `stroke: var(--gpu-wire-stroke)`.
104
- - On solid/transparent material, `--gpu-wire-stroke` comes from `textColor`; `contrast` resolves to `--label-fill`, matching filled text and solid dash.
105
- - On wireframe material, `--gpu-wire-stroke` resolves to the label-local `--gpu-wire-fill` gradient.
106
- - `#top-inlay-wire` also uses `stroke: var(--top-fill)` so dashed inlays match the wireframe label and prism wires.
156
+ - It sets `fill: none` and uses a stroke.
157
+ - On solid/transparent material, outline paint comes from `textColor`; `contrast` resolves to the palette label paint, matching filled text and solid dash.
158
+ - On wireframe material, outline paint resolves to the label-local `textWire` gradient.
159
+ - Wireframe dash uses the top-face gradient so dashed inlays match prism wires.
107
160
  - It must remain an outline around the font figures, not a single-stroke lettering replacement.
108
161
  - Keep the outline stroke thin relative to text size. The default `labelWire` is `1.1` at `textSize: 62`, safely below the ratio where counters start merging.
109
162
 
110
- Do not sample the full-face `--top-fill` ramp directly for the text outline on wireframe material; it is too large for the label geometry and makes the color read wrong. Use one text element for all text styles. Use `textColor` for solid/transparent outline paint and the label-local wire gradient for wireframe-material outline paint.
163
+ Do not sample the full-face top ramp directly for the text outline on wireframe material; it is too large for the label geometry and makes the color read wrong. Use one text element for all text styles. Use `textColor` for solid/transparent outline paint and the label-local wire gradient for wireframe-material outline paint.
111
164
 
112
- Constructor `gpuColor: "white"` and `gpuColor: "black"` remain in the static GPU example schema and map to React `textColor` in generated code. They override label paint only on solid/transparent material. On wireframe material, React `textStyle: "solid"` renders a fully opaque filled label using the text surface gradient, and `textStyle: "wireframe"` renders an outlined label using the text wire gradient.
165
+ Deprecated `gpuColor` aliases are accepted only for old snippets and map to React `textColor`. They override label paint only on solid/transparent material. On wireframe material, `textStyle: "solid"` renders a fully opaque filled label using the text surface gradient, and `textStyle: "wireframe"` renders an outlined label using the text wire gradient.
113
166
 
114
- Do not introduce `textStyle: "transparent"` as a third paint control. Legacy static configs with `gpuStyle: "transparent"` should normalize to `solid`.
167
+ Do not introduce `textStyle: "transparent"` as a third paint control. Legacy configs with `gpuStyle: "transparent"` should normalize to `solid`.
115
168
 
116
169
  Do not build filled letters from `<rect>`, `<line>`, or separate stem/bowl paths. Do not add a second label copy and do not replace the text with monoline lettering for outline mode.
@@ -1,121 +1,59 @@
1
1
  # Single-Squircle State Constructors
2
2
 
3
- This file documents the `Single Squircle States` drawer as reusable constructors. Use these recipes when another agent wants to compose a single squircle outside the full three-layer composition.
4
-
5
- ## Shared Setup
6
-
7
- Each single-state card uses:
8
-
9
- ```html
10
- <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="...">
11
- ...
12
- </svg>
3
+ This file describes reusable one-layer constructors for `SquircleScene`. Use these recipes when another agent wants one squircle outside a full multi-layer composition.
4
+
5
+ ## Base Layer Shape
6
+
7
+ ```ts
8
+ import type { SquircleLayerConfig } from "@dstackai/sqircle";
9
+
10
+ const layer: SquircleLayerConfig = {
11
+ id: "single",
12
+ base: {
13
+ material: "solid",
14
+ paletteId: "15",
15
+ text: "GPU",
16
+ textStyle: "solid",
17
+ dash: true
18
+ }
19
+ };
13
20
  ```
14
21
 
15
- The viewBox frames exactly one prism plus a small margin for hairline strokes. Single-state cards inherit the currently selected variant colors from `.single-drawer`, which receives the same CSS variables as `.hero-card`:
16
-
17
- - `--top-fill`
18
- - `--side-fill`
19
- - `--label-fill`
20
- - `--top-edge`
21
- - `--side-edge`
22
- - `--label-wire-width`
23
- - `--face-edge-width`
24
- - `--face-edge-opacity`
25
-
26
- The global transparent/wireframe switch does not affect this drawer. The drawer always shows all constructor states at once.
27
-
28
- ## Building Blocks
29
-
30
- | Part | SVG use | CSS class | Purpose |
31
- | --- | --- | --- | --- |
32
- | Solid prism | `<use href="#prism-active" />` | `.single-active` | Filled, selected-looking prism with top and side gradients |
33
- | Transparent prism | `<use href="#prism-ghost" />` | `.single-ghost` inside `.single-transparent` | Translucent filled prism |
34
- | Wireframe prism | `<use href="#prism-ghost" />` | `.single-ghost` inside `.single-wire` | Gradient outline with transparent faces |
35
- | Solid/transparent dash | `<use href="#top-inlay" />` | `.single-inlay` | Dashed top-plane squircle using `--label-fill` |
36
- | Wireframe dash | `<use href="#top-inlay-wire" />` | `.single-wire-inlay.wire-inlay` | Gradient dashed top-plane squircle |
37
- | Text label | `<use href="#label-gpu" />` | `.single-label.plane-label` | Compound path label on the top plane; the current static glyph spells `GPU` |
38
-
39
- The label always uses this transform:
40
-
41
- ```html
42
- transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)"
43
- ```
22
+ `GPU` is only example text. Any string is valid, including `"{}"` or `"CUDA"`.
44
23
 
45
24
  ## Constructor Matrix
46
25
 
47
- | State | Card class | SVG children in order | Paint behavior |
48
- | --- | --- | --- | --- |
49
- | `Solid` | `.single-card` | `.single-active` | Filled prism using selected variant top/side gradients |
50
- | `Solid Text` | `.single-card` | `.single-active`, `.single-label.plane-label` | Solid prism plus filled contrast label |
51
- | `Solid Dash` | `.single-card` | `.single-active`, `.single-inlay` | Solid prism plus contrast dashed inlay |
52
- | `Solid Text + Dash` | `.single-card` | `.single-active`, `.single-inlay`, `.single-label.plane-label` | Solid prism plus dashed inlay and filled label |
53
- | `Transparent` | `.single-card.single-transparent` | `.single-ghost` | Translucent prism at `0.38` opacity |
54
- | `Transparent Text` | `.single-card.single-transparent` | `.single-ghost`, `.single-label.plane-label` | Translucent prism plus filled label at `0.62` opacity |
55
- | `Transparent Dash` | `.single-card.single-transparent` | `.single-ghost`, `.single-inlay` | Translucent prism plus contrast dashed inlay |
56
- | `Transparent Text + Dash` | `.single-card.single-transparent` | `.single-ghost`, `.single-inlay`, `.single-label.plane-label` | Translucent prism plus dashed inlay and filled label |
57
- | `Wireframe` | `.single-card.single-wire` | `.single-ghost` | Transparent faces, gradient wire strokes |
58
- | `Wireframe Text` | `.single-card.single-wire` | `.single-ghost`, `.single-label.plane-label` | Wireframe prism plus outlined gradient label |
59
- | `Wireframe Dash` | `.single-card.single-wire` | `.single-ghost`, `.single-wire-inlay.wire-inlay` | Wireframe prism plus gradient dashed inlay |
60
- | `Wireframe Text + Dash` | `.single-card.single-wire` | `.single-ghost`, `.single-wire-inlay.wire-inlay`, `.single-label.plane-label` | Wireframe prism plus gradient dashed inlay and gradient label |
61
-
62
- ## Copy-Paste Recipes
63
-
64
- Solid with text:
65
-
66
- ```html
67
- <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Solid single squircle with text">
68
- <use class="single-active" href="#prism-active" />
69
- <use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
70
- </svg>
71
- ```
72
-
73
- Transparent with text and dash:
74
-
75
- ```html
76
- <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Transparent single squircle with text and dashed inlay">
77
- <use class="single-ghost" href="#prism-ghost" />
78
- <use class="single-inlay" href="#top-inlay" />
79
- <use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
80
- </svg>
81
- ```
82
-
83
- Wireframe with text and dash:
84
-
85
- ```html
86
- <svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Wireframe single squircle with text and dashed inlay">
87
- <use class="single-ghost" href="#prism-ghost" />
88
- <use class="single-wire-inlay wire-inlay" href="#top-inlay-wire" />
89
- <use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
90
- </svg>
91
- ```
92
-
93
- The text + dash states are reusable variants that combine the top-plane text path with a dashed inlay.
94
-
95
- In the static single-state drawer, solid and transparent text + dash states use `--label-fill` for both the filled text path and dashed inlay. Wireframe text + dash states use `--top-fill` for both the outlined text path and dashed inlay. `constructor.html` can override GPU color, GPU style, and dash color per layer because it is the GPU example; React generated code maps those controls to `textColor`, `textStyle`, and `dashColor`.
26
+ | State | Config |
27
+ | --- | --- |
28
+ | Solid | `{ material: "solid" }` |
29
+ | Solid Text | `{ material: "solid", text: "GPU", textStyle: "solid" }` |
30
+ | Solid Text Outline | `{ material: "solid", text: "GPU", textStyle: "wireframe" }` |
31
+ | Solid Dash | `{ material: "solid", dash: true }` |
32
+ | Solid Text + Dash | `{ material: "solid", text: "GPU", textStyle: "solid", dash: true }` |
33
+ | Transparent | `{ material: "transparent" }` |
34
+ | Transparent Text | `{ material: "transparent", text: "GPU", textStyle: "solid" }` |
35
+ | Transparent Text Outline + Dash | `{ material: "transparent", text: "GPU", textStyle: "wireframe", dash: true }` |
36
+ | Wireframe | `{ material: "wireframe" }` |
37
+ | Wireframe Text Filled | `{ material: "wireframe", text: "GPU", textStyle: "solid" }` |
38
+ | Wireframe Text Outline | `{ material: "wireframe", text: "GPU", textStyle: "wireframe" }` |
39
+ | Wireframe Text Outline + Dash | `{ material: "wireframe", text: "GPU", textStyle: "wireframe", dash: true }` |
40
+
41
+ The local `index.html` example renders these through `createSingleStatePresets()` in `src/pages/exampleData.ts`.
42
+
43
+ ## Paint Rules
44
+
45
+ - Solid and transparent text + dash states use palette contrast paint by default.
46
+ - Solid/transparent `textColor` and `dashColor` may be `contrast`, `white`, or `black`.
47
+ - Wireframe dash always uses the wire gradient.
48
+ - Wireframe `textStyle: "wireframe"` uses the label-local text wire gradient.
49
+ - Wireframe `textStyle: "solid"` uses the text surface gradient at full opacity.
96
50
 
97
51
  ## Ordering Rules
98
52
 
99
- - Draw the prism first.
100
- - Draw the inlay after the prism.
101
- - Draw the label last.
102
- - Use `#top-inlay` for solid/transparent states.
103
- - Use `#top-inlay-wire` for wireframe states.
104
- - Do not use two label copies. The same `.plane-label` object changes paint through CSS.
53
+ `SquircleScene` draws a variant in this order:
105
54
 
106
- ## Color Inheritance
107
-
108
- The drawer must be a sibling after the hidden variant radios. The selected variant selectors target both `.hero-card` and `.single-drawer`, e.g.:
109
-
110
- ```css
111
- #variant-15:checked ~ .hero-card,
112
- #variant-15:checked ~ .single-drawer {
113
- --top-fill: url("#top-15");
114
- --side-fill: url("#side-15");
115
- --label-fill: #f7fbff;
116
- --top-edge: #7c5fd0;
117
- --side-edge: #2d1466;
118
- }
119
- ```
55
+ 1. Prism side/top or wireframe prism.
56
+ 2. Dashed inlay, when enabled.
57
+ 3. Live SVG text, when enabled.
120
58
 
121
- See [colors.md](./colors.md) for the full palette contract.
59
+ Do not create two text copies for filled and outline states. The same live `<text>` object changes only paint.
@@ -0,0 +1,33 @@
1
+ # React Example Pages
2
+
3
+ The root HTML files are Vite shells. They should stay small and only mount React entrypoints from `src/pages`.
4
+
5
+ | HTML | React entrypoint | Purpose |
6
+ | --- | --- | --- |
7
+ | `index.html` | `src/pages/IndexPage.tsx` | Hero scene plus selectable single-squircle state drawer |
8
+ | `demo.html` | `src/pages/DemoPage.tsx` | Selectable generated gallery of 3-layer compositions |
9
+ | `constructor.html` | `src/pages/ConstructorPage.tsx` | Full constructor UI using `SquircleEditor` |
10
+ | `react.html` | `src/pages/ConstructorPage.tsx` | Compatibility alias for the constructor |
11
+
12
+ ## Source Files
13
+
14
+ - `src/pages/exampleData.ts`: shared preset and seed-layer constructors.
15
+ - `src/pages/PageShell.tsx`: shared page chrome and theme switch.
16
+ - `src/pages/pages.css`: example-page layout styles.
17
+
18
+ The examples must consume `SquircleScene`, `SquircleEditor`, palettes, and helpers from `src/squircle`. Do not paste generated SVG polygons or duplicate renderer logic in page files.
19
+
20
+ ## Behavior
21
+
22
+ - `index.html` renders a main three-layer scene and a collapsed/openable drawer of single-squircle states. Palette buttons recolor the examples through React state.
23
+ - `demo.html` renders 96 generated composition presets, including alpha palettes and solid `off`/`fluid`/`frosted` surfaces. Clicking a card changes the main hero composition. Each layer hover is a state/color swap only.
24
+ - `constructor.html` renders `SquircleEditor` with three default plain wireframe layers. The inspector exposes palette and effect controls. The Code panel exports React code using `@dstackai/sqircle` as the import path.
25
+ - `react.html` intentionally mirrors `constructor.html` for older links.
26
+
27
+ ## Rules
28
+
29
+ - Keep root HTML files as shells with one `#root` and one module script.
30
+ - Keep examples transparent-background friendly.
31
+ - Keep hover effects as opacity crossfades between base and hover variants. Do not add movement, shadows, scale, filters, or gap changes.
32
+ - Keep all squircle geometry, palette, stroke, annotation, and theme behavior in the reusable component layer.
33
+ - If a legacy static behavior is still valuable, migrate it into `src/pages` or `src/squircle`; do not edit ignored snapshots as source.