From 8a6edc1051fe6eac0bb74e6b9388d1b08a710161 Mon Sep 17 00:00:00 2001 From: Ahmad Hakim Date: Tue, 11 Aug 2026 21:51:20 +0200 Subject: [PATCH 1/3] fix: create --- assets/globals.css | 4 + assets/theme-preview.js | 151 ++++++++++++++++++++++++--------- native/lib/charts/area/v8.py | 1 + native/lib/charts/line/v8.py | 2 + native/lib/examples/card_15.py | 62 ++++++++++++++ native/pages/create.py | 19 ++++- uv.lock | 2 +- 7 files changed, 197 insertions(+), 44 deletions(-) create mode 100644 native/lib/examples/card_15.py diff --git a/assets/globals.css b/assets/globals.css index 9081ab7..ecac9a0 100644 --- a/assets/globals.css +++ b/assets/globals.css @@ -136,6 +136,10 @@ } } +.preview-theme { + font-family: var(--font-family, var(--font-sans)); +} + .toc-link[data-active="true"] { color: var(--foreground); font-weight: 500; diff --git a/assets/theme-preview.js b/assets/theme-preview.js index 8b17c67..ad897ba 100644 --- a/assets/theme-preview.js +++ b/assets/theme-preview.js @@ -13,12 +13,14 @@ var DEFAULT_RADIUS_VALUE = "__default__"; var MATCH_BASE_VALUE = "__match_base__"; var PRESET_VERSION = 2; - var BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + var BASE62_ALPHABET = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var DEFAULT_PRESET_CODE = "b0"; var BITS_V1 = { font: 5, color: 5, base: 4, radius: 4, style: 4 }; var SHIFT_V1 = (function () { - var s = {}, offset = 0; + var s = {}, + offset = 0; ["font", "color", "base", "radius", "style"].forEach(function (name) { s[name] = offset; offset += BITS_V1[name]; @@ -28,11 +30,14 @@ var BITS_V2 = { font: 5, color: 5, chart: 5, base: 4, radius: 4, style: 4 }; var SHIFT_V2 = (function () { - var s = {}, offset = 0; - ["font", "color", "chart", "base", "radius", "style"].forEach(function (name) { - s[name] = offset; - offset += BITS_V2[name]; - }); + var s = {}, + offset = 0; + ["font", "color", "chart", "base", "radius", "style"].forEach( + function (name) { + s[name] = offset; + offset += BITS_V2[name]; + }, + ); return s; })(); @@ -91,11 +96,27 @@ var styleIdx = plainIndex("style", state.style); var baseIdx = plainIndex("base", state.base); var fontIdx = plainIndex("font", state.font); - var colorIdx = state.color === MATCH_BASE_VALUE ? 0 : inheritableIndex("color", state.color); - var radiusIdx = state.radius === DEFAULT_RADIUS_VALUE ? 0 : inheritableIndex("radius", state.radius); - var chartIdx = state.chart === MATCH_BASE_VALUE ? 0 : inheritableIndex("color", state.chart); - - if (styleIdx === 0 && radiusIdx === 0 && baseIdx === 0 && colorIdx === 0 && chartIdx === 0 && fontIdx === 0) { + var colorIdx = + state.color === MATCH_BASE_VALUE + ? 0 + : inheritableIndex("color", state.color); + var radiusIdx = + state.radius === DEFAULT_RADIUS_VALUE + ? 0 + : inheritableIndex("radius", state.radius); + var chartIdx = + state.chart === MATCH_BASE_VALUE + ? 0 + : inheritableIndex("color", state.chart); + + if ( + styleIdx === 0 && + radiusIdx === 0 && + baseIdx === 0 && + colorIdx === 0 && + chartIdx === 0 && + fontIdx === 0 + ) { return DEFAULT_PRESET_CODE; } @@ -238,7 +259,6 @@ if (codeDisplay) codeDisplay.value = comboToCode(); } - var state = { base: null, color: MATCH_BASE_VALUE, @@ -327,7 +347,8 @@ values = isDark ? entry.dark : entry.light; } CHART_KEYS.forEach(function (key) { - if (values[key] !== undefined) el.style.setProperty("--" + key, values[key]); + if (values[key] !== undefined) + el.style.setProperty("--" + key, values[key]); }); } @@ -419,7 +440,8 @@ function getThemeCSS() { var base = findById("base", state.base); - var color = state.color === MATCH_BASE_VALUE ? null : findById("color", state.color); + var color = + state.color === MATCH_BASE_VALUE ? null : findById("color", state.color); var style = findById("style", state.style); var font = findById("font", state.font); if (!base || !style || !font) return ""; @@ -434,21 +456,41 @@ // both modes at once since the exported CSS needs both :root and .dark // blocks simultaneously (unlike the live apply path, which only ever // cares about whichever mode is currently active). - var chartSource = state.chart === MATCH_BASE_VALUE ? base : findById("color", state.chart); - var chartLight = {}, chartDark = {}; + var chartSource = + state.chart === MATCH_BASE_VALUE ? base : findById("color", state.chart); + var chartLight = {}, + chartDark = {}; if (chartSource) { CHART_KEYS.forEach(function (key) { - if (chartSource.light[key] !== undefined) chartLight[key] = chartSource.light[key]; - if (chartSource.dark[key] !== undefined) chartDark[key] = chartSource.dark[key]; + if (chartSource.light[key] !== undefined) + chartLight[key] = chartSource.light[key]; + if (chartSource.dark[key] !== undefined) + chartDark[key] = chartSource.dark[key]; }); } - var light = Object.assign({}, base.light, color ? color.light : {}, chartLight, style.vars, font.vars, { - "--radius": radiusValue, - }); - var dark = Object.assign({}, base.dark, color ? color.dark : {}, chartDark, style.vars, font.vars, { - "--radius": radiusValue, - }); + var light = Object.assign( + {}, + base.light, + color ? color.light : {}, + chartLight, + style.vars, + font.vars, + { + "--radius": radiusValue, + }, + ); + var dark = Object.assign( + {}, + base.dark, + color ? color.dark : {}, + chartDark, + style.vars, + font.vars, + { + "--radius": radiusValue, + }, + ); function serialize(values) { return Object.keys(values) @@ -459,7 +501,13 @@ .join("\n"); } - return ":root {\n" + serialize(light) + "\n}\n\n.dark {\n" + serialize(dark) + "\n}"; + return ( + ":root {\n" + + serialize(light) + + "\n}\n\n.dark {\n" + + serialize(dark) + + "\n}" + ); } window.preview = { @@ -478,8 +526,10 @@ // whichever one(s) the user hasn't already manually overridden. upsertSyntheticOption(colorSelect, MATCH_BASE_VALUE, baseLabel); upsertSyntheticOption(chartSelect, MATCH_BASE_VALUE, baseLabel); - if (state.color === MATCH_BASE_VALUE && colorSelect) colorSelect.value = MATCH_BASE_VALUE; - if (state.chart === MATCH_BASE_VALUE && chartSelect) chartSelect.value = MATCH_BASE_VALUE; + if (state.color === MATCH_BASE_VALUE && colorSelect) + colorSelect.value = MATCH_BASE_VALUE; + if (state.chart === MATCH_BASE_VALUE && chartSelect) + chartSelect.value = MATCH_BASE_VALUE; applyBase(); applyColorOverlay(); @@ -532,15 +582,19 @@ afterStateChange(); }, - shuffle: function (seed) { var registries = window.__THEME_REGISTRIES__; if (!registries) return; - var rng = mulberry32(seed === undefined ? (Math.random() * 0xffffffff) >>> 0 : seed); - - state.style = registries.style[Math.floor(rng() * registries.style.length)].id; - state.base = registries.base[Math.floor(rng() * registries.base.length)].id; - state.font = registries.font[Math.floor(rng() * registries.font.length)].id; + var rng = mulberry32( + seed === undefined ? (Math.random() * 0xffffffff) >>> 0 : seed, + ); + + state.style = + registries.style[Math.floor(rng() * registries.style.length)].id; + state.base = + registries.base[Math.floor(rng() * registries.base.length)].id; + state.font = + registries.font[Math.floor(rng() * registries.font.length)].id; state.color = rng() < 0.5 ? MATCH_BASE_VALUE @@ -675,8 +729,21 @@ } } } - }); + // --- 6. COPY PRESET CODE ACTION --- + if (e.target.closest && e.target.closest("#copy-preset-button")) { + navigator.clipboard.writeText(window.preview.getPresetCode()); + + var label = document.getElementById("preset-copy-text"); + if (label) { + var original = label.textContent; + label.textContent = "Copied!"; + setTimeout(function () { + label.textContent = original; + }, 1000); + } + } + }); // document.addEventListener("click", function (e) { // if (e.target.id === "copy-css-preset") { @@ -718,8 +785,10 @@ break; } } - }).observe(document.documentElement, { attributes: true, attributeFilter: ["class"] }); - + }).observe(document.documentElement, { + attributes: true, + attributeFilter: ["class"], + }); // On first load: if the URL has a valid ?preset= code, apply it. // Otherwise fall back to plain defaults (and write "b0" to the URL so @@ -737,9 +806,13 @@ upsertSyntheticOption( document.getElementById("color-theme-select"), MATCH_BASE_VALUE, - baseEntry ? baseEntry.label : "Match base" + baseEntry ? baseEntry.label : "Match base", + ); + upsertSyntheticOption( + document.getElementById("radius-select"), + DEFAULT_RADIUS_VALUE, + "Default", ); - upsertSyntheticOption(document.getElementById("radius-select"), DEFAULT_RADIUS_VALUE, "Default"); syncSelectsToState(); afterStateChange(); } diff --git a/native/lib/charts/area/v8.py b/native/lib/charts/area/v8.py index d03eb36..62f68f9 100644 --- a/native/lib/charts/area/v8.py +++ b/native/lib/charts/area/v8.py @@ -92,6 +92,7 @@ def create_gradient(var_name): stroke_width=2, stack_id="1", is_animation_active=False, + active_dot={"fill": f"var({s[2]})", "stroke": f"var({s[2]})"}, ) for s in series ], diff --git a/native/lib/charts/line/v8.py b/native/lib/charts/line/v8.py index c55ed7a..9c934e1 100644 --- a/native/lib/charts/line/v8.py +++ b/native/lib/charts/line/v8.py @@ -40,6 +40,7 @@ def line_chart_footer_legend(): type_="natural", dot=False, is_animation_active=False, + active_dot={"fill": "var(--chart-1)", "stroke": "var(--chart-1)"}, ), rx.recharts.line( data_key="mobile", @@ -48,6 +49,7 @@ def line_chart_footer_legend(): type_="natural", dot=False, is_animation_active=False, + active_dot={"fill": "var(--chart-2)", "stroke": "var(--chart-2)"}, ), rx.recharts.x_axis( data_key="month", diff --git a/native/lib/examples/card_15.py b/native/lib/examples/card_15.py new file mode 100644 index 0000000..2bc2a0b --- /dev/null +++ b/native/lib/examples/card_15.py @@ -0,0 +1,62 @@ +import reflex as rx + +from components.ui.card import card +from native.templates.masonary import masonry_card + + +@masonry_card(label="General") +def card_15() -> rx.Component: + colors = [ + {"bg": "bg-background", "label": "-background", "border": True}, + {"bg": "bg-foreground", "label": "-foreground"}, + {"bg": "bg-primary", "label": "-primary"}, + {"bg": "bg-secondary", "label": "-secondary", "border": True}, + {"bg": "bg-muted", "label": "-muted", "border": True}, + {"bg": "bg-accent", "label": "-accent", "border": True}, + {"bg": "bg-border", "label": "-border", "border": True}, + {"bg": "bg-chart-1", "label": "-chart-1"}, + {"bg": "bg-chart-2", "label": "-chart-2"}, + {"bg": "bg-chart-3", "label": "-chart-3"}, + {"bg": "bg-chart-4", "label": "-chart-4"}, + {"bg": "bg-chart-5", "label": "-chart-5"}, + ] + + def swatch(color: dict) -> rx.Component: + border_class = "border border-input" if color.get("border") else "" + return rx.el.div( + rx.el.div( + class_name=f"rounded-lg {color['bg']} {border_class} size-12 flex-shrink-0", + ), + rx.el.p( + color["label"], + class_name="text-xs text-muted-foreground font-theme truncate w-full text-center", + ), + class_name="flex flex-col items-center gap-y-2 flex-1 min-w-0", + ) + + rows = [colors[:6], colors[6:]] + + return card.root( + card.header( + card.title( + "Theme Colors", + class_name="text-lg font-semibold text-foreground text-center", + ), + card.description( + "Dynamic palette variants", + class_name="text-sm font-light text-muted-foreground text-center", + ), + ), + card.content( + rx.el.div( + *[ + rx.el.div( + *[swatch(c) for c in row], + class_name="w-full flex flex-row gap-x-2", + ) + for row in rows + ], + class_name="flex flex-col gap-y-4", + ), + ), + ) diff --git a/native/pages/create.py b/native/pages/create.py index 67be651..9932e56 100644 --- a/native/pages/create.py +++ b/native/pages/create.py @@ -25,6 +25,7 @@ from native.lib.examples.card_12 import card_12 from native.lib.examples.card_13 import card_13 from native.lib.examples.card_14 import card_14 +from native.lib.examples.card_15 import card_15 from native.registry.colors import COLOR_THEMES from native.registry.fonts import FONT_REGISTRY from native.registry.radii import RADII_OPTIONS @@ -102,7 +103,7 @@ def _option_label(opt: dict) -> str: def open_preset() -> rx.Component: return dialog.root( dialog.trigger( - button("Open Preset", variant="outline", class_name="justify-start") + button("Open Preset", variant="outline", class_name="justify-center") ), dialog.popup( dialog.header( @@ -211,19 +212,28 @@ def _sidebar_desktop(): id="shuffle-button", type="button", variant="outline", - class_name="w-full justify-start", + class_name="w-full justify-center", ), button( - rx.el.span("--preset ", html_for="preset-code-display"), + rx.el.span( + "--preset ", + html_for="preset-code-display", + id="copy-preset-text", + ), rx.el.input( id="preset-code-display", default_value="b0", read_only=True, disabled=True, + class_name=( + "w-12 shrink-0 bg-transparent border-none outline-none p-0 " + "text-center disabled:opacity-100 disabled:cursor-default" + ), ), + id="copy-preset-button", variant="outline", type="button", - class_name="w-full flex flex-row items-center justify-start", + class_name="w-full flex flex-row items-center justify-center", ), open_preset(), class_name="p-4 flex flex-col gap-3 bg-card/20", @@ -285,6 +295,7 @@ def preview_space(): masonry_card( lambda: card.root(card.content(line_chart_footer_legend())) )(), + card_15(), class_name=" ".join( [ "preview-theme", diff --git a/uv.lock b/uv.lock index 254dcdc..07cc833 100644 --- a/uv.lock +++ b/uv.lock @@ -94,7 +94,7 @@ wheels = [ [[package]] name = "buridan" -version = "0.0.2" +version = "0.0.3" source = { editable = "." } [package.dev-dependencies] From a7c0cdb19408d4a148de91e6cc00b64d29509da0 Mon Sep 17 00:00:00 2001 From: Ahmad Hakim Date: Wed, 12 Aug 2026 09:08:46 +0200 Subject: [PATCH 2/3] fixes to create event triggers --- assets/theme-preview.js | 19 ++++---- native/lib/examples/card_15.py | 1 + native/pages/create.py | 85 +++++++++++++++++++++++----------- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/assets/theme-preview.js b/assets/theme-preview.js index ad897ba..39356b3 100644 --- a/assets/theme-preview.js +++ b/assets/theme-preview.js @@ -256,7 +256,7 @@ function afterStateChange() { updateUrl(comboToCode()); var codeDisplay = document.getElementById("preset-code-display"); - if (codeDisplay) codeDisplay.value = comboToCode(); + if (codeDisplay) codeDisplay.textContent = "--preset " + comboToCode(); } var state = { @@ -630,6 +630,13 @@ }, applyAll: applyAll, getThemeCSS: getThemeCSS, + + syncAll: function () { + if (!ensureDefaults()) return; + syncSelectsToState(); + applyAll(); + afterStateChange(); + }, }; document.addEventListener("change", function (e) { @@ -707,8 +714,6 @@ var target = document.getElementById("get-css-theme"); if (target) { target.textContent = getThemeCSS(); - - // Re-run the swatches on the reverted defaults! updateThemeSwatches(); } } @@ -732,14 +737,12 @@ // --- 6. COPY PRESET CODE ACTION --- if (e.target.closest && e.target.closest("#copy-preset-button")) { - navigator.clipboard.writeText(window.preview.getPresetCode()); - - var label = document.getElementById("preset-copy-text"); + var label = document.getElementById("preset-code-display"); if (label) { - var original = label.textContent; + navigator.clipboard.writeText(window.preview.getPresetCode()); label.textContent = "Copied!"; setTimeout(function () { - label.textContent = original; + label.textContent = "--preset " + window.preview.getPresetCode(); }, 1000); } } diff --git a/native/lib/examples/card_15.py b/native/lib/examples/card_15.py index 2bc2a0b..a6662e4 100644 --- a/native/lib/examples/card_15.py +++ b/native/lib/examples/card_15.py @@ -59,4 +59,5 @@ def swatch(color: dict) -> rx.Component: class_name="flex flex-col gap-y-4", ), ), + class_name="mx-auto w-full max-w-sm", ) diff --git a/native/pages/create.py b/native/pages/create.py index 9932e56..df0d643 100644 --- a/native/pages/create.py +++ b/native/pages/create.py @@ -141,6 +141,47 @@ def open_preset() -> rx.Component: ) +def reset_confirm() -> rx.Component: + return dialog.root( + dialog.trigger( + button("Reset", type="button", class_name="w-full", variant="destructive") + ), + dialog.popup( + dialog.header( + dialog.title("Reset Theme"), + dialog.description( + "This will discard your current customizations and return to the default preset." + ), + ), + dialog.footer( + rx.el.div( + dialog.close( + button( + "Cancel", + type="button", + variant="outline", + class_name="flex-1", + ), + class_name="flex-1", + ), + dialog.close( + button( + "Reset", + type="button", + variant="destructive", + class_name="flex-1", + id="reset-preset-button", + ), + class_name="flex-1", + ), + class_name="w-full flex flex-row items-center justify-center gap-x-4", + ), + ), + class_name="sm:max-w-sm", + ), + ) + + def _sidebar_desktop(): return rx.el.aside( rx.el.div( @@ -215,37 +256,17 @@ def _sidebar_desktop(): class_name="w-full justify-center", ), button( - rx.el.span( - "--preset ", - html_for="preset-code-display", - id="copy-preset-text", - ), - rx.el.input( - id="preset-code-display", - default_value="b0", - read_only=True, - disabled=True, - class_name=( - "w-12 shrink-0 bg-transparent border-none outline-none p-0 " - "text-center disabled:opacity-100 disabled:cursor-default" - ), - ), + rx.el.span("--preset b0", id="preset-code-display"), id="copy-preset-button", variant="outline", type="button", - class_name="w-full flex flex-row items-center justify-center", + class_name="w-full flex flex-row items-center justify-center gap-1", ), open_preset(), class_name="p-4 flex flex-col gap-3 bg-card/20", ), rx.el.div( - button( - "Reset", - id="reset-preset-button", - type="button", - class_name="w-full", - variant="destructive", - ), + reset_confirm(), class_name="p-3 bg-card/20", ), class_name=( @@ -273,7 +294,10 @@ def preview_space(): rx.el.div( rx.el.div( masonry_card( - lambda: card.root(card.content(area_chart_with_gradient())) + lambda: card.root( + card.content(area_chart_with_gradient()), + class_name="mx-auto w-full max-w-sm", + ) )(), card_01(), card_02(), @@ -283,7 +307,10 @@ def preview_space(): card_06(), card_07(), masonry_card( - lambda: card.root(card.content(bar_chart_multiple())) + lambda: card.root( + card.content(bar_chart_multiple()), + class_name="mx-auto w-full max-w-sm", + ) )(), card_08(), card_09(), @@ -293,7 +320,10 @@ def preview_space(): card_13(), card_14(), masonry_card( - lambda: card.root(card.content(line_chart_footer_legend())) + lambda: card.root( + card.content(line_chart_footer_legend()), + class_name="mx-auto w-full max-w-sm", + ) )(), card_15(), class_name=" ".join( @@ -375,7 +405,8 @@ def create_page(): style: {json.dumps(STYLE_REGISTRY)}, font: {json.dumps(FONT_REGISTRY)}, }}; - if (window.preview) window.preview.applyAll(); + // if (window.preview) window.preview.applyAll(); + if (window.preview) window.preview.syncAll(); """ ), rx.call_script( From dcb7bc3b2f5de455f68af6f209ffd3aa0787be19 Mon Sep 17 00:00:00 2001 From: Ahmad Hakim Date: Wed, 12 Aug 2026 09:09:30 +0200 Subject: [PATCH 3/3] update markdown files for changed docs --- assets/docs/charts/area-chart.md | 1 + assets/docs/charts/line-chart.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/assets/docs/charts/area-chart.md b/assets/docs/charts/area-chart.md index fb44cdb..94ad406 100644 --- a/assets/docs/charts/area-chart.md +++ b/assets/docs/charts/area-chart.md @@ -655,6 +655,7 @@ def area_chart_with_gradient(): stroke_width=2, stack_id="1", is_animation_active=False, + active_dot={"fill": f"var({s[2]})", "stroke": f"var({s[2]})"}, ) for s in series ], diff --git a/assets/docs/charts/line-chart.md b/assets/docs/charts/line-chart.md index 9caaffc..fa58ba9 100644 --- a/assets/docs/charts/line-chart.md +++ b/assets/docs/charts/line-chart.md @@ -514,6 +514,7 @@ def line_chart_footer_legend(): type_="natural", dot=False, is_animation_active=False, + active_dot={"fill": "var(--chart-1)", "stroke": "var(--chart-1)"}, ), rx.recharts.line( data_key="mobile", @@ -522,6 +523,7 @@ def line_chart_footer_legend(): type_="natural", dot=False, is_animation_active=False, + active_dot={"fill": "var(--chart-2)", "stroke": "var(--chart-2)"}, ), rx.recharts.x_axis( data_key="month",