diff --git a/guides/data-apps/themes.mdx b/guides/data-apps/themes.mdx index 45c2c87c..af068fcf 100644 --- a/guides/data-apps/themes.mdx +++ b/guides/data-apps/themes.mdx @@ -51,6 +51,85 @@ You create and manage themes from the **Themes** page in **Settings**. Bootstrapping a theme with an agent (for example, asking an LLM to draft CSS rules and a `skill.md` against these guidelines) is a quick way to get a usable first version. From there, iterate on the instructions until the agent produces apps that match what you have in mind. +## Manage themes as code + +You can download, version, review, and upload themes through the same organization content-as-code workflow used for custom roles, users, and groups: + +```bash +lightdash download --organization +# Edit or add lightdash/themes// +lightdash upload --organization +``` + +This lets you move an existing UI-created theme into version control or create a theme locally. Organization download includes every theme and writes each one under its immutable slug: + +```text +lightdash/ + themes/ + acme-brand/ + lightdash-theme.yml + css/ + theme.css + fonts/ + acme-sans.woff2 + images/ + logo.svg + instructions/ + skill.md +``` + +The directory name must exactly match the manifest's `slug`. Every manifest field is required; use `null` for an empty description or extra instructions: + +```yaml lightdash/themes/acme-brand/lightdash-theme.yml +codeVersion: 1 +slug: acme-brand +name: Acme Brand +description: Brand theme for customer-facing data apps +extraInstructions: |- + Use the horizontal logo in page headers. + Keep data-dense tables compact. +``` + +To create a new theme locally, choose a new lowercase, hyphenated slug, create this manifest and any asset folders you need, then run `lightdash upload --organization`. Upload creates or updates the theme by manifest slug. Treat the slug as immutable; change `name` when you only want to rename the theme for display. + + + Organization mode has no theme-only selector. Before uploading, review custom roles, users, and groups under the same `lightdash/` directory as well as your themes. + + +### Files and validation + +The CLI validates every local theme before changing any organization resource. A theme package accepts: + +- `.css` files under `css/` +- `.woff`, `.woff2`, `.ttf`, and `.otf` files under `fonts/` +- `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, and `.svg` files under `images/` +- Markdown files under `instructions/` + +Files must live directly inside one of these folders. Nested folders, symlinks, unknown paths, unsafe filenames, unsupported file types, empty files, and files whose contents don't match their extension are rejected. The manifest is limited to 64 KiB, each asset to 10 MB, all assets combined to 100 MB, and the complete uncompressed package to 110 MB. + +Don't commit or upload restricted Apple system-font binaries such as SF-family or New York fonts. Use a system font stack instead: + +```css +font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; +``` + +### Synchronization behavior + +- A successful download replaces the complete local `themes/` directory with the remote theme set. If any theme cannot be downloaded or validated, your previous local themes are preserved. +- Upload preflights every local theme before applying custom roles, users, groups, or themes. One invalid theme prevents all organization changes in that run. +- An existing slug is updated, a new slug is created, and a theme whose manifest and files already match is skipped as unchanged. +- A missing or empty local `themes/` directory is a no-op. Removing one local theme directory does not delete the remote theme. +- Upload preserves each theme's UUID and whether it is the organization default. Set the default or delete a theme in the Lightdash UI. +- Each theme import is atomic, but a multi-theme upload is not one transaction. If a later import fails, themes that completed earlier remain applied and the CLI reports the partial failure. + +There is no standalone `lightdash themes` command, theme-specific `--only` flag, or local theme preview. If an organization theme endpoint returns `404`, check that the Lightdash CLI and server versions both support theme packages. + +### Evaluate your changes + +Uploading proves the package is valid, but it doesn't rebuild or restyle existing data app versions. To see whether you like a theme, select it when generating a new data app or apply it to an existing app from the theme picker above the prompt. Lightdash creates a new app version that you can compare with the previous version. + +If you edit themes with an AI coding agent, install or update the [Lightdash agent skills](/guides/developer/agent-skills). The existing `developing-in-lightdash` skill teaches the agent this package format and the safe organization download/upload workflow. The agent can verify the CLI round trip without browser access; you still evaluate the generated app visually in Lightdash. + ## Setting a default theme You can mark one theme as the default for your organization. New data apps will use the default theme unless you pick a different one. diff --git a/guides/developer/agent-skills.mdx b/guides/developer/agent-skills.mdx index 2323c751..e8f20b83 100644 --- a/guides/developer/agent-skills.mdx +++ b/guides/developer/agent-skills.mdx @@ -100,6 +100,7 @@ Once installed, your AI coding agent automatically picks up the Lightdash skills - Fix validation errors in your semantic layer - Refactor existing metric definitions - Add joins between tables +- Create, edit, and synchronize organization Data App themes as code ### Example prompts @@ -115,6 +116,11 @@ Add a customer_segment dimension based on the lifetime_value field Generate the Lightdash YAML configuration for my users dbt model ``` +``` +Download our organization content, update the acme-brand Data App theme with +the new logo and typography rules, upload it safely, and verify the CLI round trip +``` + The agent uses the installed skills to generate correct, up-to-date Lightdash configurations. ## Keeping skills updated diff --git a/guides/developer/dashboards-as-code.mdx b/guides/developer/dashboards-as-code.mdx index e9cb59c0..69690d9e 100644 --- a/guides/developer/dashboards-as-code.mdx +++ b/guides/developer/dashboards-as-code.mdx @@ -1049,7 +1049,7 @@ Agent config as code is **opt-in**: a bare `lightdash download` does not include ## Organization content as code -Three organization-scoped resources — **custom roles**, **users**, and **groups** — can also be downloaded and uploaded as YAML using the `--organization` mode of `lightdash download` and `lightdash upload`. This is a separate workflow from project content (charts, dashboards, virtual views, and data apps) and requires an **organization admin**. +Four organization-scoped resources — **custom roles**, **users**, **groups**, and **Data App themes** — can also be downloaded and uploaded using the `--organization` mode of `lightdash download` and `lightdash upload`. This is a separate workflow from project content (charts, dashboards, virtual views, and data apps) and requires an **organization admin**. A single `lightdash download --organization` writes every organization resource to a top-level folder in your working directory: @@ -1061,14 +1061,27 @@ lightdash/ .yml groups/ .yml + themes/ + / + lightdash-theme.yml + css/ + fonts/ + images/ + instructions/ ``` -A single `lightdash upload --organization` reads all three folders and applies them **sequentially in dependency order**: custom roles first, then users, then groups. A failed phase prevents the dependent phases from running, so a user file can safely reference a custom role added in the same commit, and a group file can safely list a user added in the same commit. +A single `lightdash upload --organization` preflights all local theme packages, then applies resources **sequentially in dependency order**: custom roles first, then users, groups, and themes. A failed phase prevents later phases from running, so a user file can safely reference a custom role added in the same commit and a group file can safely list a user added in the same commit. One invalid local theme prevents any organization resource from changing. - Organization mode cannot be combined with project-content flags such as `--charts`, `--dashboards`, `--project`, `--nested`, or `--force`. Uploads never delete resources in Lightdash — remove unwanted roles, users, or groups in the UI. + Organization mode cannot be combined with project-content flags such as `--charts`, `--dashboards`, `--project`, `--nested`, or `--force`. Uploads never delete resources in Lightdash — remove unwanted roles, users, groups, or themes in the UI. +### Data App themes as code + +Themes are strict multi-file packages under `lightdash/themes//`, rather than single YAML documents. Organization download exports UI-created themes into the canonical package layout. Organization upload creates or updates themes by manifest slug, skips unchanged packages, and leaves remotely existing themes alone when their local directory is absent. + +Theme upload does not change the organization default. Each theme import is atomic, but the complete multi-theme upload is not one transaction. For the manifest, supported assets, validation limits, synchronization behavior, and visual evaluation workflow, see [Manage themes as code](/guides/data-apps/themes#manage-themes-as-code). + ### Custom roles as code [Custom roles](/references/workspace/custom-roles) are managed as code. Unlike charts and dashboards, custom roles are **organization-scoped** rather than project-scoped, so downloading and uploading them uses the `--organization` mode of `lightdash download` and `lightdash upload` and requires an organization admin. diff --git a/references/lightdash-cli.mdx b/references/lightdash-cli.mdx index 0a86f7c9..d422f06c 100644 --- a/references/lightdash-cli.mdx +++ b/references/lightdash-cli.mdx @@ -820,7 +820,7 @@ You can make changes to the code and upload these changes back to your Lightdash - opt-in flag to download every AI agent in the project as code. The endpoint is paginated, so the CLI walks all pages automatically. Agent files are written to `lightdash/ai-agents/`. Bare `lightdash download` does not include agents. - `--organization` - (default: false) - - switch to organization mode and download [organization-scoped content](/guides/developer/dashboards-as-code#organization-content-as-code) — [custom roles](/guides/developer/dashboards-as-code#custom-roles-as-code), [users](/guides/developer/dashboards-as-code#users-as-code), and [groups](/guides/developer/dashboards-as-code#groups-as-code) — instead of project content. Files are written to `lightdash/custom-roles/.yml`, `lightdash/users/.yml`, and `lightdash/groups/.yml`. Requires organization admin permissions. If the groups feature is disabled on the instance, the groups folder is skipped (run with `--verbose` for the explanation). Cannot be combined with project-content flags such as `--charts`, `--dashboards`, `--project`, or `--nested`. + - switch to organization mode and download [organization-scoped content](/guides/developer/dashboards-as-code#organization-content-as-code) — [custom roles](/guides/developer/dashboards-as-code#custom-roles-as-code), [users](/guides/developer/dashboards-as-code#users-as-code), [groups](/guides/developer/dashboards-as-code#groups-as-code), and [Data App themes](/guides/data-apps/themes#manage-themes-as-code) — instead of project content. Files are written to `lightdash/custom-roles/.yml`, `lightdash/users/.yml`, `lightdash/groups/.yml`, and `lightdash/themes//`. Theme packages are staged and validated before the local `themes/` directory is replaced, so a failed theme download preserves the previous local theme set. A successful download removes local theme directories that no longer exist remotely. Requires organization admin permissions. If the groups feature is disabled on the instance, the groups folder is skipped (run with `--verbose` for the explanation). Cannot be combined with project-content flags such as `--charts`, `--dashboards`, `--project`, or `--nested`. **Examples:** @@ -896,6 +896,8 @@ Download only a specific AI agent by slug (or UUID). ```bash lightdash download --agents orders-support-agent +``` + Download all charts and dashboards, plus every virtual view in the project. ```bash @@ -906,7 +908,9 @@ Download only specific virtual views by slug. ```bash lightdash download --virtual-views orders_enriched customers_enriched -Download every [organization-scoped resource](/guides/developer/dashboards-as-code#organization-content-as-code) as code — custom roles, users, and groups (requires org admin). +``` + +Download every [organization-scoped resource](/guides/developer/dashboards-as-code#organization-content-as-code) as code — custom roles, users, groups, and Data App themes (requires org admin). ```bash lightdash download --organization @@ -984,7 +988,7 @@ If there have been changes made to a chart or dashboard in the application that - skip uploading AI agents even when `lightdash/ai-agents/` contains files. Use this to keep an agents folder in your repo without pushing changes on every `lightdash upload`. - `--organization` - (default: false) - - switch to organization mode and upload [organization-scoped content](/guides/developer/dashboards-as-code#organization-content-as-code) — [custom roles](/guides/developer/dashboards-as-code#custom-roles-as-code), [users](/guides/developer/dashboards-as-code#users-as-code), and [groups](/guides/developer/dashboards-as-code#groups-as-code) — instead of project content. Reads YAML files from `lightdash/custom-roles/`, `lightdash/users/`, and `lightdash/groups/` and applies them sequentially in that order (custom roles → users → groups), so a user file can reference a custom role and a group file can reference a user from the same upload. Each resource is upserted by its portable identity (role name, primary email, group name) — creating, updating, or reporting no change per file. Requires organization admin permissions. Cannot be combined with project-content flags such as `--charts`, `--dashboards`, `--project`, or `--force`. Uploads never delete roles, users, or groups. + - switch to organization mode and upload [organization-scoped content](/guides/developer/dashboards-as-code#organization-content-as-code) — [custom roles](/guides/developer/dashboards-as-code#custom-roles-as-code), [users](/guides/developer/dashboards-as-code#users-as-code), [groups](/guides/developer/dashboards-as-code#groups-as-code), and [Data App themes](/guides/data-apps/themes#manage-themes-as-code) — instead of project content. The CLI validates every local theme before making any organization change, then applies custom roles → users → groups → themes. Document resources are upserted by role name, primary email, or group name; themes are upserted by manifest slug and skipped when unchanged. A missing or empty `lightdash/themes/` directory is a no-op, and removing one theme directory never deletes the remote theme. Theme imports preserve the organization default and are atomic per theme, not per batch. Requires organization admin permissions. Cannot be combined with project-content flags such as `--charts`, `--dashboards`, `--project`, or `--force`. Uploads never delete roles, users, groups, or themes. - `--send-invites` - (default: false) - only valid with `--organization`. When uploading users, send invitation emails to eligible staged users. Users who have already authenticated, are disabled, or already have a valid outstanding invitation are skipped. Without this flag, users authenticate through the instance's existing domain, SSO, or manually triggered invitation flows — credentials are never carried in the YAML. @@ -1071,8 +1075,6 @@ Fork a data app into a new record in the current project. lightdash upload --apps revenue-explorer --create-new ``` -Upload every [organization-scoped resource](/guides/developer/dashboards-as-code#organization-content-as-code) — custom roles, users, and groups — back to the organization (requires org admin). - Upload every AI agent file under `lightdash/ai-agents/` (creates missing agents, updates existing ones by slug). ```bash @@ -1083,7 +1085,9 @@ Upload only a specific AI agent by slug. ```bash lightdash upload --agents orders-support-agent -Upload every custom role in `lightdash/custom-roles/` back to the organization (requires org admin). +``` + +Upload every [organization-scoped resource](/guides/developer/dashboards-as-code#organization-content-as-code) — custom roles, users, groups, and Data App themes — back to the organization (requires org admin). ```bash lightdash upload --organization diff --git a/references/workspace/custom-roles.mdx b/references/workspace/custom-roles.mdx index 0ac4512b..ad5d6b99 100644 --- a/references/workspace/custom-roles.mdx +++ b/references/workspace/custom-roles.mdx @@ -284,7 +284,7 @@ scopes: Malformed files, unknown scopes, and scopes that aren't allowed at the role's level are rejected per file with the file path and error message; other files continue to process. The CLI exits with a non-zero status if any file fails validation. -Organization users and groups can be managed through the same `--organization` workflow, and uploads apply resources sequentially — custom roles first, then users, then groups — so a user file can safely reference a custom role added in the same commit. See [Organization content as code](/guides/developer/dashboards-as-code#organization-content-as-code) for the full workflow, [Users as code](/guides/developer/dashboards-as-code#users-as-code), and [Groups as code](/guides/developer/dashboards-as-code#groups-as-code). +Organization users, groups, and Data App themes can be managed through the same `--organization` workflow. The CLI preflights themes, then uploads custom roles, users, groups, and themes in that order, so a user file can safely reference a custom role added in the same commit. See [Organization content as code](/guides/developer/dashboards-as-code#organization-content-as-code) for the full workflow, [Users as code](/guides/developer/dashboards-as-code#users-as-code), [Groups as code](/guides/developer/dashboards-as-code#groups-as-code), and [Data App themes as code](/guides/data-apps/themes#manage-themes-as-code). ## Troubleshooting