Theme
Version
GitHub PyPI Discord
On this page

Develop Citry UI

This page is for contributors who change Citry UI itself: fix a component, add an input, or update a component's docs page. To use Citry UI in your application, see Installation instead.

Citry UI lives in the main Citry repository, under packages/py/citry_ui/. You work on it with the same tools as the rest of Citry.

Set up the repository

Follow Set up your machine in the Citry development guide. It installs uv, Rust, Node.js, and pnpm, clones the repository, and runs these two commands from the repository root:

uv sync --all-packages
pnpm install

uv sync --all-packages installs citry-ui in editable mode together with its test dependencies, so your changes take effect without a reinstall. pnpm install adds the Node tools that build Citry UI's CSS and JavaScript files and type-check its templates.

Find a component's files

Each component family has its own directory under packages/py/citry_ui/citry_ui/components/. A family is one component or a few that work together, such as CTabs, CTab, and CTabPanel. The directory name is the family's name with a c prefix, for example ctabs/ or calert_dialog/.

FileWhat it holds
c<name>.pyThe components: their inputs and slots, event handlers, template, and messages
runtime.source.css, runtime.source.jsReadable CSS or JavaScript, when the family keeps them in files
runtime.min.css, runtime.min.jsMinified files built from the source files. The package ships these
api.mdThe public docs page for the family
api.ymlThe API reference tables that the docs build adds below api.md
snippets/Example modules that api.md shows as live previews
tests/The family's browser tests
quality/Example states for the quality tools
README.mdNotes for maintainers

A few families, such as Button and Dialog, keep their CSS and JavaScript as strings in their Python module instead of in separate files.

The full design of each family, including its accessibility and keyboard rules, lives in docs/design/ui_components/, in a file named after the family with hyphens, such as tabs.md or alert-dialog.md. Update it when you change what a component does.

The COMPONENTS tuple in citry_ui/components/__init__.py lists every component in order. Citry UI registers only the components in that tuple, so add a new component there.

Edit CSS and JavaScript

When a family has runtime.source.css or runtime.source.js, edit only the source file. Then rebuild the minified files from the repository root:

pnpm citry-ui:build-assets

Commit the rebuilt .min files with your change. To check that they match their source files without writing anything, run:

pnpm citry-ui:check-assets

python scripts/check.py runs the same check, and fails when you edit a source file without rebuilding.

Change default text

The labels a component shows by default, such as a close button's accessible name, live in the messages block at the end of its class. After you add or change a message, rebuild the translation catalog that ships with the package:

uv run --no-sync python -m citry_ui_i18n._generate_catalog

Every message also needs an entry in the translations table of the family's api.yml. The package tests fail when the catalog or api.yml does not match the component.

Update the docs page

The docs page for a component is its api.md file. The docs site reads it directly from the component directory, adds the API tables from api.yml, and publishes it under /ui-library/components/. The sidebar groups come from docs_site/ui_library.yml, so a new family needs an entry there.

To preview the page while you edit, start the docs server and open the component's page:

uv run --no-sync python -m docs_site serve

How to embed a live example is described in the docs site README.

Run the tests

Run all Citry UI tests that do not need a browser:

uv run --no-sync pytest packages/py/citry_ui/tests \
  packages/py/citry_ui/citry_ui -m "not e2e"

These include the checks that catch most mistakes in a component change:

  • test_component_contracts.py checks that each component's CSS variables, data-* attributes, and parts match its api.yml and its design document.
  • test_citry_ui_type_check.py runs citry check --types over every component, so a typo in a template expression or in the component's JavaScript fails the test. It needs the Node tools from pnpm install, and skips without them.
  • test_asset_budgets.py fails when the CSS or JavaScript of all components together, raw or compressed, grows past a fixed size limit. When the growth is intended, raise the limit in the test and say why in your pull request.
  • test_i18n_catalog.py checks the translation catalog and the translation keys in api.yml.

Run the browser tests

The browser tests click through each component in Chromium. Install the browser test dependencies and the browser once:

uv sync --all-packages --group e2e
uv run --no-sync playwright install chromium

Then run one family's tests, for example Tabs:

uv run --no-sync pytest \
  packages/py/citry_ui/citry_ui/components/ctabs \
  -m e2e --browser chromium

To run every Citry UI browser test the way CI does:

uv run --no-sync pytest \
  packages/py/citry_ui/citry_ui/components \
  packages/py/citry_ui/citry_ui/quality/tests/e2e \
  packages/py/citry_ui/tests/e2e \
  -m e2e --browser chromium -n 4 --dist loadfile

-n 4 runs the tests in four parallel processes. Lower it on a machine with fewer CPUs.

Run all checks

Before you open a pull request, run the full repository check described in Run the checks:

python scripts/check.py

Use the quality tools

packages/py/citry_ui/citry_ui/quality/ holds tools that check all components together. They are not part of the published package. Most families have a quality/scenario.py that describes example states, each one the component rendered with particular inputs, for these tools to check.

Common commands, run from the repository root:

# List every example state as JSON
uv run --no-sync python -m citry_ui.quality.scenarios

# Render one state as a full HTML page
uv run --no-sync python -m citry_ui.quality.routes \
  tabs.overview --output /tmp/tabs.html

# Report the size of each family's CSS and JavaScript
uv run --no-sync python -m citry_ui.quality.asset_report

The quality README lists the rest, including HTML validation, screenshots, and checks of the built package.

Edit Citry UI in VS Code

When you work on Citry UI's templates without an application around them, point the VS Code extension at the library directly. Add this to your VS Code settings:

{
  "citry.app": "citry_ui:__citry_library__"
}

The editor then reads Citry UI's component names, inputs, slots, and template data. When you also need your application's own components or settings, point citry.app at your application's Citry instance instead.

Read the component rules

Citry UI components follow stricter rules than ordinary components, for example on how a family is designed, named, and translated. Read the Citry UI component policy before you add a component or change a component's inputs.