Skip to content

feat(toolbar): manual theme editor with presets#77

Merged
sarahdayan merged 29 commits intomainfrom
feat/manual-editor
Mar 12, 2026
Merged

feat(toolbar): manual theme editor with presets#77
sarahdayan merged 29 commits intomainfrom
feat/manual-editor

Conversation

@sarahdayan
Copy link
Member

@sarahdayan sarahdayan commented Mar 10, 2026

Summary

Adds a full visual theme editor to the toolbar's Theme tab, driven by the variable catalog from @experiences/theme. Users can customize all autocomplete CSS variables through type-appropriate controls, preview changes live, toggle between light and dark mode, and reset to defaults. Eight curated presets provide distinct starting points across different aesthetics.

Enregistrement.de.l.ecran.2026-03-11.a.08.53.13.mov

This PR also cleans up the theme data model: cssVariables moves from per-widget params to the experience level, and the redundant themeMode API field is removed (the mode is inferred from the data shape).

See the Manual Theme Editor workstream for full context.

Note

This PR depends on https://github.com/algolia/experience-management-api/pull/405 being merged and deployed

Exit criteria (workstream)

All theme variables from the Zod schema are editable in the toolbar

All autocomplete variables are rendered as interactive controls in the Theme tab. The editor reads the variable catalog and renders the appropriate control for each type.

Controls match variable types (color picker, slider, text input)

  • Color variables get a hex color picker with RGB triplet conversion
  • Numeric variables with constraints get sliders with min/max/step
  • Shadow variables get a structured multi-layer editor with per-layer offset, blur, spread, color, and opacity controls

Variables are grouped by domain with collapsible sections

Variables are folded under collapsible groups. Each header shows a badge with the count of modified variables.

Changes preview live on the page without saving

CSS custom properties update in real time via a <style> element injected on :root.

Light/dark mode toggle works and updates all color variables

A mode switcher is always visible at the top of the editor, with light/dark tabs to edit each mode independently:

  • Adaptive mode maintains separate light and dark overrides
  • Fixed mode uses a single set (useful for customers whose site doesn't adapt to the user's preferred color scheme)

Reset to default works per-variable and globally

Modified variables show an inline reset button. A global "Reset all" button restores every variable to catalog defaults. Modified state is visually indicated per-variable.

Theme changes persist via the Experience API on save

Overrides are stored at the experience level as cssVariables. The theme mode (adaptive vs. fixed) is inferred from the data shape rather than stored as a separate field.

Beyond exit criteria

  • 9 curated presets with a dropdown selector and color swatch previews. Each preset provides distinct light and dark overrides across colors, typography, borders, shadows, spacing, and more.
  • Theme tab gated on autocomplete widget presence with an empty state that links to the Manual tab.
  • Paintbrush shortcut in autocomplete widget card headers to navigate directly to the Theme tab.
  • Cleanup: cssVariables moved from per-widget params to experience level, redundant themeMode API field removed, example theme switcher removed.

sarahdayan and others added 14 commits March 10, 2026 12:00
- Add `@experiences/theme` package with `ThemeVariable` types and `generateThemeCss()` for generating light/dark CSS custom property blocks
- Integrate theme CSS generation into runtime middleware, replacing ad-hoc cssVariables injection
- Replace bundled satellite.css + upstream autocomplete.css with a local autocomplete.css, dropping the unused satellite theme
- Add `ThemeSwitcher` component to the React example for dev/demo theming
- Add `examples/shared/` with reusable theme definitions across JS and React examples
- Improve dev setup: local proxy serves dist files during development, file watching triggers HMR, `useAlgoliaExperiences` supports relative URLs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add createThemeOverridesSchema() that builds a Zod validation schema
from the theme variable catalog. Number variables get min/max/step
constraints, all fields include descriptions with units and defaults
for AI agent consumption. ThemeOverrides now accepts string | number
values with coercion at the CSS generation boundary.

Reorganize packages/theme into types.ts, lib/, and widgets/ for
clearer separation of concerns. Move autocomplete.css into
packages/runtime/src/styles/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Test createThemeOverridesSchema validation (type checking, numeric
constraints, empty/partial objects), JSON Schema output (property keys,
types, descriptions, constraints), and generateThemeCss (light/dark
blocks, defaults, unit appending, unitless numbers, overrides,
per-mode overrides, unknown keys ignored).

Also add toJsonSchema() method and zod-to-json-schema dependency for
AI tool definition generation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The stylesheet is a product of the theme variable catalog, not the
runtime. Moving it to packages/theme/src/widgets/ co-locates it with
the variable definitions it consumes. The runtime build config now
reads the CSS from the theme package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add "Format: R, G, B." to color variable descriptions so agents know
to return RGB triplets. Add min/max constraints (0–1) to all
alpha and opacity variables to prevent out-of-range values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap the overrides schema in `{ light, dark }` so agents return per-mode
values in a single tool call. Add constraints to `base-unit` and change
`font-weight-medium` from text to number with valid CSS weight range.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert 7 variables from unvalidated text to number type with min/max/step
constraints: font weights, header font size, item line height, transition
duration, detached modal top offset, and scrollbar color mix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace freeform text shadow variables with a constrained `shadow` type
that stores an array of structured layer objects (offsetX, offsetY, blur,
spread, color, opacity). This makes shadows validatable by the schema and
easier for AI agents to produce correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lations

The `step` constraint is a UI hint for slider increments, not a
validation rule. IEEE 754 rounding causes values like 0.15 to fail
`multipleOf(0.1)`, rejecting valid agent output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rrideValue

Move isShadowLayers to its own predicates/ folder, strengthen the check
to require both offsetX and blur, and add unit tests. Export
ThemeOverrideValue from types.ts to remove the duplicate declaration in
generate-theme-css.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move test files from a top-level __tests__/ directory to colocated
__tests__/ folders within each source module (lib/, predicates/).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sarahdayan sarahdayan requested a review from dhayab as a code owner March 10, 2026 17:58
@sarahdayan sarahdayan changed the title Manual theme editor with presets feat(toolbar): manual theme editor with presets Mar 10, 2026
sarahdayan and others added 6 commits March 10, 2026 19:08
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 8 curated theme presets (Default, Minimal, Bold, Soft, Warm,
Nature, Elegant, Brutalist) with a preset selector UI in the theme
editor. Each preset provides distinct light and dark overrides for
colors, typography, borders, shadows, spacing, and more.

- Add ThemePreset type and preset definitions in theme package
- Add PresetSelector component with color swatch previews
- Wire preset application through panel and app components
- Move autocomplete files under autocomplete/ subfolder
- Remove redundant theme switcher from examples
- Fix hexToRgbTriplet spacing, shadow field double label,
  generateThemeCss null safety, and onSave dependency array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the redundant `themeMode` field from the API payload and
ExperienceApiResponse type. The mode is now inferred from the
cssVariables shape: flat object means fixed, `{ light, dark }` means
adaptive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Theme CSS is now injected once from the top-level experience config
instead of per-widget. Remove the onCssVariableChange callback chain,
CssVariablesEditor component, and cssVariables from widget parameters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show an empty state with a link to the manual tab when no autocomplete
widget is present in the experience.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show a paintbrush icon in the autocomplete block card header (next to
locate and delete) that navigates to the Theme tab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sarahdayan and others added 2 commits March 11, 2026 10:04
Neutral gray, compact preset for unbranded site search that blends into any page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Base automatically changed from feat/theme-system to main March 11, 2026 15:45
# Conflicts:
#	examples/react/src/App.tsx
#	packages/runtime/src/experiences/middleware.ts
#	packages/runtime/src/experiences/widget.tsx
#	packages/runtime/tsdown.config.ts
#	packages/theme/package.json
#	packages/theme/src/lib/__tests__/create-theme-overrides-schema.test.ts
#	packages/theme/src/lib/__tests__/generate-theme-css.test.ts
#	packages/theme/src/lib/generate-theme-css.ts
#	packages/theme/src/types.ts
@netlify
Copy link

netlify bot commented Mar 12, 2026

Deploy Preview for algolia-experiences-js ready!

Name Link
🔨 Latest commit 108bb88
🔍 Latest deploy log https://app.netlify.com/projects/algolia-experiences-js/deploys/69b2e18a2f161c00086478f2
😎 Deploy Preview https://deploy-preview-77--algolia-experiences-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Mar 12, 2026

Deploy Preview for algolia-experiences-react ready!

Name Link
🔨 Latest commit 108bb88
🔍 Latest deploy log https://app.netlify.com/projects/algolia-experiences-react/deploys/69b2e189a7ab4f00084ce820
😎 Deploy Preview https://deploy-preview-77--algolia-experiences-react.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

The merge brought in main's flat `autocomplete.ts` and `autocomplete.css`
which are superseded by the subdirectory structure (`autocomplete/variables.ts`,
`autocomplete/presets.ts`, `autocomplete/autocomplete.css`). Remove the
duplicates and update the shared example import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sarahdayan and others added 3 commits March 12, 2026 14:54
The toolbar's Theme tab replaces the dev theme switcher.
The React example was cleaned up during the merge but the
JS example was missed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Missed search.html and product.html in the previous cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mponent

The toolbar's Theme tab replaces the dev theme switcher.
Remove examples/shared/, the Vite alias, and the unused
React ThemeSwitcher component.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sarahdayan and others added 2 commits March 12, 2026 16:51
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Loading an experience with a custom theme no longer marks variables as
dirty. "Reset all" now restores the initially loaded theme rather than
switching to the Default preset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sarahdayan sarahdayan requested a review from dhayab March 12, 2026 15:54
Copy link
Member

@dhayab dhayab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 awesome!

@sarahdayan sarahdayan merged commit 9800347 into main Mar 12, 2026
8 checks passed
@sarahdayan sarahdayan deleted the feat/manual-editor branch March 12, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants