Install and adopt¶
Install¶
The package carries the stylesheet, the Jinja macros, the linter and the token reference. It has no front-end toolchain and does not want one.
Vendor the stylesheet¶
That writes two files into static/css/ads/:
static/css/ads/
├── ads.css # the one a page links — knobs, the reset and every part, in order
└── situ.css # only if you use situ, and only after that kit's own stylesheet
Then link it:
One file, one request. The package authors the stylesheet as a tree — knobs.css, derive.css,
base.css and a dozen files under parts/ — and sync splices them in ads.css's own import
order. That order is load-bearing (layout.css and then forced-colors.css last), and it stays
declared in exactly one place.
Why one file instead of @import?
@import is a render-blocking waterfall: the browser cannot discover parts/*.css until
ads.css has arrived and been parsed. A fifteen-file tree therefore costs two serial round
trips before anything paints, and fifteen cache entries afterwards — on every navigation, if
the server answers a conditional request with a fresh 200, as several do.
The parts are how the stylesheet is authored. They are not how it is served.
Why vendored rather than served from the package?
A browser needs a URL.
Vendoring normally opens an escape hatch — someone edits the local copy and the project
quietly forks. abilian-ds lint closes it by hashing every vendored file against what the
package would produce, so an edit is a finding — and so is forgetting to re-sync after
an upgrade, which is the more common way to get there:
static/css/ads/ads.css:0: asset-drift: differs from the installed package
(not re-synced after an upgrade, or edited locally); run `abilian-ds sync`
sync also deletes any other .css under the target: the directory belongs to the command,
and a stale parts/button.css left over from an older version is exactly the file someone
edits and wonders why nothing changes.
Configure the project¶
Paths live in pyproject.toml, so the commands take no flags:
[tool.abilian-ds]
templates = ["templates"] # what to lint
theme = "static/css/ads" # where sync put the stylesheet
components = "templates/ads" # where sync put the macros
allow = ".abilian-ds-allow" # project allowlist, optional
app = "myapp.main:app" # optional, enables the endpoint check
Configure Jinja¶
from jinja2 import StrictUndefined
TemplateConfig(
directory=[HERE / "templates"],
engine=JinjaTemplateEngine,
engine_callback=lambda engine: setattr(
engine.engine, "undefined", StrictUndefined
),
)
StrictUndefined is not optional
It is what turns a misspelt component argument into an error instead of an unstyled
element. {{ button("Save", variant="pirmary") }} raises; without it, it renders a
button with no classes and nobody notices until a screenshot.
Run the linter¶
Green means on-system. Wire it into CI and into your pre-commit hook; it is the contract, and everything else in these docs is a description of it.
Next¶
- Your first page — a real page, with the shell.
- Theming it — make it look like your client's product.