brickwork positioning
Status: canonical positioning source. Updated 2026-09-18 by owner
direction: public lead is Beautiful by default with the customer
outcome line below; G1 ("One interface system…") retired as the public
lead. ADR-108 ownership line (2026-09-15), visual-bar honesty rules
(2026-09-11), and the greenfield review (docs/plans/positioning-greenfield-3.33.md,
icvoss/django-brickwork#614) remain as supporting history.
Scope: every brickwork-facing copy surface (README.md, brickworkui.com,
PyPI description, any future landing page or pitch) derives its claims from
this document and must not diverge from it. Where a surface currently
contradicts it, that surface is wrong; see "Required downstream fixes" below.
This is not itself marketing copy. It is an internal reference: the position, the evidence for each claim, and the boundaries. Anyone writing brickwork-facing prose lifts claims from here, with the evidence attached.
Product shape: umbrella ADR-108
(docs/adrs/ADR-108-brickwork-three-product-split.md in the ICV OSS
umbrella). The package owns substrate, reference catalogue, and starter.
brickworkui.com owns flagship sell page compositions as a designed consumer.
1. Position statement
brickwork is the server-rendered Django interface foundation: one branded, accessible design system for the surfaces a team ships (public sites, product applications, data-heavy operations, documentation, editorial publishing and transactional journeys). It supplies foundations, components, shells, Theme profiles, interaction patterns and copyable page archetypes on Tailwind 4, Alpine 3 and HTMX 2. Django is its only hard runtime dependency.
Category: interface foundation for hand-built Django UI. It is not a publishing platform, a CMS, a page builder, a Django-admin skin, or a general Tailwind utility layer. Interface families in INTERFACE-SYSTEM.md are coverage targets, not brand identity.
That package offer is three jobs in one distribution (ADR-108). Public copy must name which job a claim is about:
| Package job | Job | Consumer verb |
|---|---|---|
| Substrate | Tokens, shells, polished primitives, layout structures, interaction contracts, WCAG/a11y gates, brand override by --bw-* |
install, pin, compose, override tokens |
| Reference catalogue | Archetypes and compositions that prove coverage, plus Alpine/HTMX wiring; copy and own | browse, copy, adapt |
| Installable template | manage.py startsite emits a running project that is yours |
emit once |
Showcase ownership (brickworkui.com). The flagship site owns its brand pack and its sell page compositions. It proves the package by composing it; it does not redefine the package as whatever the homepage needs. Missing reusable primitives are package work. Finished theatre for one brand is site page work (ADR-056 consumer pages), not a second kit and not an automatic package example.
Do not attribute showcase sell craft to a substrate include API. Do not treat a copied example as a package page contract (ADR-056). Do not treat starter output as supported surface (ADR-095).
Shipping vs target. Public copy must show the family shipping matrix (verified catalogue counts), not a blanket "complete" or "not yet complete" line. At 3.33.0 the catalogue ships Documentation 7/7 and Editorial 7/7 required archetypes, with Product applications the deepest family (15 archetypes), Data-heavy operations at 7/7, Marketing and public web at 7/7, and Transactional journeys at 7/7. Remaining INTERFACE-SYSTEM gaps concentrate in marketing/public-web breadth and transactional journeys (plus specialist viz fitting the data contract). Cite section 5 gated counts when quoting inventory.
It is brand-agnostic by construction: every visual value is a --bw-*
custom property, so a consumer rebrands it by overriding tokens, never by
touching component classes (docs/BRANDING.md:3-4). Brickwork Theme
(THEME.md) is the named profile ladder (L1 Recolour through L4
Rhythm) on that mechanism.
2. Who it is for
A Django team that wants one branded, accessible design system across the interfaces it ships, without rebuilding common design decisions for each new site, app, ops console, docs surface, auth flow or content workflow. It is not a Django-admin skin, a page builder, a CMS, or a general Tailwind utility layer (see Boundaries, section 6).
3. The lead claim
Beautiful by default. Frontend your Django site fast, confident it delivers an excellent experience across devices.
Support (subordinate, not competing leads): Pin the foundation. Own the
pages. Theme with --bw-* profiles (L1 to L4). One interface system for the
Django surfaces you ship.
Retired as public leads (2026-09-18): G1 "One interface system for the Django surfaces you ship" (demoted to support). Earlier retired leads (2026-09-16): "Beautiful interfaces for anything." and "Brickwork. Building blocks for beautiful apps and websites." Those may appear in historical artefacts; new copy uses the lead above. "Anything" overclaims partial marketing and transactional coverage and must not return as an H1 hedge.
Honesty on "beautiful". The lead is the customer promise for package defaults: finished craft out of the box, judged against VISUAL-BAR.md and the cited audits below. It is not a warranty that every consumer page is beautiful. Composition choices, content and hierarchy on copied pages remain the consumer's; evidence for the package aim is the meet-bar and beat scorecards, not inventory counts or axe alone.
Package defaults aim at finished interfaces that can stand next to leading
Tailwind UI kits on the jobs in VISUAL-BAR.md. That aim is
evidenced by the independent rendered meet-bar pass
audits/2026-09-12-visual-bar-signoff-pass.md
(tip e79f828), not by composition examples, axe gates or inventory counts
alone. Residual tracked misses named in that audit stay honest; they are not
soft-passed. Marketing footer link-group vocabulary (#500) is corrected in
the substrate-ready honesty pass after that sign-off tip.
On the beat win bar, prefer Brickwork only where the evidence says so. An independent review on package-default CSS recorded Brickwork leading or tying Tailwind Plus on 6 of 8 VISUAL-BAR surfaces (audits/2026-09-14-beat-win-scorecard-s5s7.md): ties on S1, S2, S4, S6, S7 and S8; no surface Lead. S5 marketing landing remains Lose (Plus still leads product media depth); do not soft-pass it to Tie. S3 app form stays N/S (Plus still is partial marketing contact chrome, not a matched Application UI create-form); do not invent Plus Lead or Tie for S3. Soft-pass is forbidden. Meet-bar PASS and beat 6/8 PASS are separate cites; neither revokes the other.
An independent rendered design review judges hierarchy, content, spacing, typography, navigation, relevant states and responsive composition. Source composition, accessibility checks and accurate inventory are separate evidence. None of those alone certifies visual quality or makes arbitrary consumer pages beautiful.
The competitive visual bar is stamped in VISUAL-BAR.md: a frozen
panel of leading Tailwind UI kits (look and idea only; clean-room on --bw-*),
fixed surfaces, scorecard axes, and done criteria. Public copy must not name
those kits as brickwork's identity (section 9) and must never claim a house
aesthetic (section 10).
Site-branded embedded renders have a different framing and are assessed separately. The paired finished-application/public-site review remains work under icvoss/brickworkui.com#60 and #64.
The composition evidence. 54 of the 55 examples are pure composition: zero
bespoke CSS, built entirely from shipped tokens and components. One
exception: src/brickwork/examples/app/date-range-picker.html adds a <style>
block. Every value in it is an existing --bw-* token (no new colour
invented), it is scoped under .bw-drp so it cannot leak into a host page,
it carries a comment stating this CSS must never be added to the shipped
stylesheet, and it exists specifically because brickwork ships no date picker
engine (BR-BW-INPUT-004 is a Fixed rule: no package-maintained
bw_date_picker tag or Alpine calendar behaviour exists and none ever will;
src/brickwork/examples/app/date-range-picker.html:5-11). The substrate does
ship _date_picker_chrome.html (field shell, optional range layout, trigger
and panel chrome only) so consumers are not left inventing that wrapper;
the example's calendar script and grid remain consumer-owned copy-paste.
Three further inline styles exist in that same file, all display:none /
visibility:hidden, structural rather than cosmetic
(src/brickwork/examples/app/date-range-picker.html:632,697,817).
Say "no date picker engine" / "no bw_date_picker Alpine component", never
"no date picker". A developer who copies that example has a working date
range picker: a calendar popover with weekday and month grids, locale-aware
via Django's own django.utils.dates, single-date mode included, over a
native <input type="date"> no-JS floor that stays the submitted control at
all times. What brickwork declines to ship is the maintained JS calendar
engine, not the capability, and not the field/panel chrome. This is the
delivery model in miniature and the clearest illustration of the lead claim:
the substrate (including date picker chrome) plus an example gets a consumer
a real date picker they own outright, with no engine contract for brickwork
to maintain or break.
Why the examples are safe to give away. ADR-056 (referenced at
src/brickwork/examples/README.md:7, CHANGELOG.md:616-620): examples are
the consumer's to own, never a contract they extend. Pages are copy-paste,
and src/brickwork/examples/ sits off the Django template-loader path, so a
consumer cannot extend one by accident. The examples state this rule in their
own headers: "It is not on the template loader path, so you cannot extend it
(ADR-056)" (src/brickwork/examples/app/date-range-picker.html:5-6).
The starter supplies a running starting composition (ADR-095).
manage.py startsite emits wired settings, a contrast-verified brand file,
and populated marketing, dashboard and documentation pages with their views.
That establishes a working starting point for a consumer. It does not certify
its visual quality or the consumer's final result. Review the actual emitted
pages at the target viewports and with the intended content.
The command is a governed, versioned surface. Its output belongs to the consumer from the moment it is written, with no update path back into it (ADR-056, ADR-095). It adds no catalogue item. The visual-quality requirement and the copy-ownership boundary are separate obligations.
Never introduce a competitor's name as the shape of what brickwork is. The owner explicitly raised and rejected "Tailwind for Django" as a lead this session. Position fresh, with the examples as evidence, not by analogy.
4. Supporting claims, in order
4.1 The upgrade boundary (the mechanism under the lead)
Substrate, components and shells upgrade under semver; a consumer's own
pages, copied from the examples, are theirs and never touched by an upgrade.
This is what makes "copy the example, then diverge freely" safe: the
copied page is not on the template-loader path, so there is no version of
brickwork that silently changes it (ADR-056, src/brickwork/examples/README.md:7).
4.2 Tested accessibility (the credibility floor)
State the mechanism, never the word "guarantee". A CI gate cannot guarantee conformance; it can run consistently and be shown to catch real defects.
| What runs | Scope |
|---|---|
| axe-core WCAG 2.2 AA scan | 260 documents across two fixture sets: 154 hand-maintained pages (77 fixtures x light and dark) plus 106 catalogue-document pages (53 catalogue documents, being 52 archetypes plus 1 skeleton, x light and dark), blocking every push (a11y-gate CI job) |
| No-JS floor suite | blocking |
| Keyboard suites | blocking |
| Mobile-overflow checks | 4 widths: 320, 360, 375, 414px, blocking |
| Pixel-level composited contrast measurement | canvas getImageData, blocking |
| Tap-target size measurement | getBoundingClientRect(), every fixture at 375px, blocking (a11y/axe.spec.mjs, icvoss/django-brickwork#208) |
| Coarse-pointer touch-target measurement | getBoundingClientRect() under a hasTouch: true browser context, marketing header nav/actions, marketing footer links, breadcrumb links, blocking (icvoss/django-brickwork#242) |
Two-tier target size. The package's claimed conformance level is WCAG 2.2
AA, and 2.5.8 Target Size (Minimum) at AA requires 24x24 CSS px; every
interactive control and navigational link meets that floor unconditionally,
gated by the tap-target sweep above. A second tier goes further than the AA
claim: the marketing header nav, the marketing header actions slot, the
marketing footer links, and the breadcrumb trail take a min-block-size of
--bw-size-touch-target-min (2.75rem/44px, the WCAG 2.5.5 AAA and
platform-HIG bar) under @media (pointer: coarse), so a touch-primary device
gets the larger target while a fine-pointer desktop render is unchanged.
Gated against the second row above: the coarse-pointer measurement
asserts >= 44px on those four surfaces and asserts the fine-pointer render
stays under 44px (a regression pin, not a new claim). State it as "44px on
coarse-pointer devices for these four nav surfaces", never a bare "44px
targets" or "AAA conformance": the package's claimed level stays AA.
Automated testing has a ceiling. Deque's published figure is that automated tooling catches roughly 57% of accessibility issues. That figure is Deque's, not brickwork's; it appears nowhere in this repo's own testing, and must never be presented as something brickwork measured. State it as a limitation of the axe-core claim, not a brickwork statistic.
The strongest proof is the gate's own recorded miss. The repository's own
test comments record a real 4.25:1 contrast defect that the axe gate ran
green over (a11y/axe.spec.mjs:361-374, tracked as issue #118). The reason
is structural, and worth stating: axe's contrast check does not rasterise the
page, so for text painted over a background image it reports "incomplete"
rather than a violation. The scrim was a gradient, so the composited ratio
depended on where a line of text fell: the heading passed while the lede sat
at 4.25:1 at the same time. That miss is why pixel-level contrast measurement
was added.
Admitting the gate missed something once, and naming what was added because
of it, is more persuasive than claiming the gate never misses. Use this in
copy rather than omit it.
RTL has structural proof, not a tested fixture. Logical-property counts
(4.3 below) are real structural evidence for RTL support, but there is no
dir="rtl" accessibility fixture in the 144-document gate. State this
distinction; do not imply RTL is axe-tested.
4.3 Brickwork Theme (token profiles)
Consumers build a Brickwork Theme by overriding --bw-* tokens, never by
touching component classes (docs/THEME.md, ADR-110). 358 unique
--bw-* tokens exist (dated at 3.12.0), 313 overridable. 10 are load-bearing,
of which 8 are unconditional.
L1 Recolour (the load-bearing colour minimum plus fg-on-accent, light and
dark) is roughly 16 lines of CSS and recolours the derived colour cascade
live via color-mix(). That is not a full product brand. Deeper profiles are
normative levels on the same substrate:
| Level | What it adds |
|---|---|
| L1 Recolour | Load-bearing colours + fg-on-accent |
| L2 Voice | Font stacks (optional type roles) |
| L3 Material | Radius and/or elevation and/or surface ladder |
| L4 Rhythm | Spacing base and/or density |
Say "L1 recolour in ~16 lines"; never "16 lines rebrands the whole system." Base-theme already ships full default scales (radius, spacing, elevation, type); higher levels replace selected values. See brand-pack.md.
L3 / L4 public claims (Phase G, after #600): you may say an authored L3
profile changes kit chrome (radius, elevation, raised surfaces on card,
button, and modal) on a white canvas, because Phase E proved token-compliance
for those surfaces
(audits/2026-09-15-theme-phase-e-token-compliance.md).
You may say L4 sets system rhythm via --bw-space-1 (Tailwind projection)
and/or density. Do not claim L3/L4 makes defaults beautiful: kit craft
remains VISUAL-BAR.md / beat F.2. Overlay steps beyond the
L3 minimum (--bw-radius-xl, --bw-elevation-4 / -5) must be authored when
you want modal/toast altitude to follow (THEME.md).
Dark mode is an authored surface, not a computed inversion: data-theme
dark values are authored per token, not derived from light
(BR-BW-TOK-002, docs/BRANDING.md). Four theme axes are verified
working: brand (data-bw-brand), theme (data-theme), density (3 token
files), direction.
RTL precision. "Zero physical left/right properties" is true at the
property level: layout is built entirely on logical properties (273 in
source CSS, 500 in the compiled dist). It is not true at the value level:
one documented exception exists, background-position: right / left at
frontend/src/components.css:384,391, explicitly RTL-flipped at line 390
because background-position has no logical equivalent. State the exception
whenever the "zero" claim is made; a bare "zero" is disprovable in devtools.
5. The full verified numbers (source: code, drift-gated)
Rows marked gated are asserted by tests/test_positioning.py against the
shipped artefact or importable code at test time: a future edit to either the
code or this table that leaves them disagreeing fails CI. Rows marked
dated are mechanically derived as of the stated version but not cheaply
assertable in pytest without running Node or hand-listing every source file;
they carry the version at which they were counted so staleness is visible on
sight, and are refreshed by hand at the next audit.
| Fact | Value | Note |
|---|---|---|
| Components | 84 | 58 core, 26 marketing. Gated against catalogue-manifest.json |
| Shells | 6 | base, app, auth, centred, docs, marketing. Gated against catalogue-manifest.json |
| Sections | 44 | Gated against catalogue-manifest.json |
| Archetypes | 52 | Gated against catalogue-manifest.json. examples/base.html moved out of this count into its own skeleton kind (icvoss/django-brickwork#464): it is a raw document skeleton a consumer copies, not a complete page, and was the only archetype ever carrying family: null |
| Template tag registrations | 32 total | 21 inclusion_tag, 10 simple_tag, 1 filter. Write "21 component tags" or state the 32 total; never a bare "21 template tags". Gated by importing the templatetags libraries and counting register.tags/register.filters |
| Tokens | 358 unique --bw-* | 313 overridable; 10 load-bearing, 8 unconditional. Overridable count gated against token-manifest.json; the 358 total (all custom properties in compiled tokens.css) is dated at 3.12.0 |
| Alpine components | 16 | bwDropdown, bwTabs, bwModal, bwToastRegion, bwToast, bwCombobox, bwDismissible, bwTooltip, bwTagInput, bwDropzone, bwSidebarCollapse, bwSlideOver, bwTableSelection, bwSortable, bwThemeSwitch, bwCodeCopy. Gated by parsing the Alpine.data(...) calls in frontend/src/js/index.js's single registration point |
| Examples | 96 | 52 archetype pages, 44 sections. Gated against catalogue-manifest.json |
| Icons | 50 vendored Lucide SVG files | exposed as 53 callable names (3 aliases). State precisely; never a bare "50" or bare "53". Dated at 3.10.0 |
| Tests | 1023 test functions | across 58 files containing at least one def test_ (90 Python files exist under tests/ in total; most are fixtures, conftest or helpers with no test functions of their own). Dated at 3.10.0 (counted via git grep -hE '^def test_' -- tests \| wc -l for the function count, git grep -lE '^def test_' -- tests \| wc -l for the file count); not gated, this count moves with every PR |
| A11y gate | 274 axe-scanned documents | 168 hand-maintained (84 fixtures x light and dark) plus 106 catalogue-document (53 catalogue documents x light and dark), blocking CI. Catalogue-document fixture count gated against catalogue-manifest.json's archetype plus skeleton counts; the 168 hand-maintained fixtures are gated against the real a11y/generate_fixtures.py run's own written output by tests/test_a11y_fixture_coverage.py (icvoss/django-brickwork#226), so this figure can never drift from what the axe gate actually loads. Hand-maintained fixture COVERAGE (does every shell/component/section have at least one fixture) is separately gated by the same file against catalogue-manifest.json. The 53 to 55 fixture-file step (icvoss/django-brickwork#272, plus icvoss/django-brickwork#275's bw_ranked_list fixture landing independently on main) added theme-switch-compact-<theme>.html, the layout="compact" no-JS floor, and the ranked-list fixture: the pre-existing no-JS coverage only ever rendered layout="inline". The 55 to 56 step (icvoss/django-brickwork#185) added data-table-empty-cta-<theme>.html, the _data_table.html empty-state action CTA. The 56 to 57 step (the chart card work) added chart-card-<theme>.html, covering the real {% bw_chart_mount %} tag's accessible-name pairing plus the card's loading, error and empty states. The 57 to 58 step (the sparkline work) added sparkline-<theme>.html, covering both tones, the highlight marker and the no-JS floor. The 58 to 59 step (the trend indicator work, VIZ-017) added trend-indicator-<theme>.html, covering the up/down/flat states of the standalone _trend_indicator.html partial extracted from _stat.html's own trend block. The 60 to 61 step (the scorecard/stat-comparison work, VIZ-011/012/019/020) added scorecard-<theme>.html, covering the shared dashboard grid's span= modifiers arranging real _stat.html cards, plus _stat_comparison.html's sm/md/lg sizes each paired with a different trend direction. The 61 to 62 step (ADR-091, icvoss/django-brickwork#439) added docs-<theme>.html, the new docs shell's own fixture: a populated two-column [article | rail] page with real long-form .bw-prose content and a real {% bw_nav %} rail, the shell that had no fixture at all until this step. The 62 to 63 step (icvoss/django-brickwork#448 item 1) added docs-with-site-chrome-<theme>.html, exercising docs_site_header_region/docs_site_footer_region, the site-wide chrome seam outside <main> no other docs fixture had rendered. The 63 to 65 step (the code-display work, icvoss/django-brickwork#259) added a code-display-<theme>.html/code-display-js-<theme>.html pair: a plain panel, a filename+language panel and a copyable panel with real multi-line source, plus the JS leg proving bwCodeCopy's own init() reveals the copy control the no-JS floor ships hidden. The 65 to 66 step (the mobile-nav toggle work, icvoss/django-brickwork#263) added mobile-nav-toggle-<theme>.html, covering the marketing_nav_region include pattern. The 66 to 67 step (appearance suite card pilot, icvoss/django-brickwork#535) added card-<theme>.html, covering surface/elevation and header_recipe/footer_recipe chrome on _card.html. The 68 to 73 step (marketing header overlay, icvoss/django-brickwork#565) added five marketing-overlay-* fixtures per theme (no-JS opaque floor, stamped dark/light-scrolled/dark-scrolled contexts, plus the JS PE boot leg). The 73 to 74 step (token specimen, icvoss/django-brickwork#268) added token-specimen-<theme>.html, the dual-pane load-bearing brand token grid with contrast-pair chips. The 74 to 75 step (preview frame, icvoss/django-brickwork#269) added preview-frame-<theme>.html, the surface-guarded live-render container. The 75 to 76 step (proof collage, icvoss/django-brickwork#572) added proof-collage-<theme>.html, the marketing kit collage / page-as-proof section. The 76 to 77 step (version switcher, icvoss/django-brickwork#414) added version-switch-<theme>.html, covering {% bw_version_switch %} current-version disclosure, aria-current marking and latest/deprecated status text. The 77 to 78 step (docs toc, icvoss/django-brickwork#627) added toc-<theme>.html, covering {% bw_toc %} on-this-page landmark, nested __sub entries and aria-current="location". The 78 to 79 step (three-region app shell, icvoss/django-brickwork#700 to #703) added app-regions-<theme>.html, covering layout="regions" with an icon-density rail (link plus menu trigger), contextual sidebar and empty-track reclaim. The 79 to 83 step (marketing sustainment, BR-BW-MKT-007..009, icvoss/django-brickwork#570/#571) added four fixture pairs: soft-stage-overlay-<theme>.html, composing the soft-stage atmosphere with the overlay header on the same document (BR-BW-MKT-007 rule 3); soft-stage-media-behind-<theme>.html, composing soft-stage with media_placement="behind" (BR-BW-MKT-007 rule 3, ADR-057 section 1a; the stacking-order proof is a11y/marketing_stacking.spec.mjs); product-shot-placement-<theme>.html, the product-shot include inside a hero media slot at both "beside" and the "below" default (BR-BW-MKT-008); and marketing-reveal-<theme>.html, a section with reveal="enter" wrapping a feature grid, the resting (no-JS) export (BR-BW-MKT-009; the reduced-motion computed-style proof is a11y/marketing_reveal.spec.mjs). The 83 to 84 step (hero width="bleed", icvoss/django-brickwork#710) added hero-bleed-soft-stage-<theme>.html, composing width="bleed" with atmosphere="soft-stage" under the overlay header (the geometry proof is a11y/marketing_bleed.spec.mjs). The archetype half then moved from 32 to 36 (16 to 18 catalogue archetypes) when the Data-heavy operations family shipped queue.html and audit-trail.html, then from 36 to 40 (18 to 20 catalogue archetypes) when the same family shipped report.html and comparison.html, then from 40 to 42 (20 to 21 catalogue archetypes) when it shipped analysis-dashboard.html, then from 42 to 46 (21 to 23 catalogue archetypes) when the Documentation family opened with docs/home.html and docs/article.html (icvoss/django-brickwork#408, icvoss/django-brickwork#409). The count then held at 46 when examples/base.html was reclassified from archetype to its own skeleton kind (icvoss/django-brickwork#464): it is a raw document skeleton a consumer copies, not a complete page, so the published archetype figure drops from 23 to 22, but the document is still scanned. The archetype half then moved from 46 to 56 catalogue-document pages (23 to 28 catalogue documents) when five further archetypes shipped: app/error-maintenance.html, app/onboarding.html, app/status-tracker.html, ops/dense-list.html and marketing/comparison.html. Beat Phase D then moved the catalogue-document half from 56 to 58 pages (28 to 29 catalogue documents) by adding the toast journey archetype app/toast.html (the modal journey ships as section sections/modal/confirm.html, so it does not add a catalogue-document page). The Documentation family's API reference, table of contents, navigation and search-results archetypes (icvoss/django-brickwork#410, #413, #412, #411) then moved the catalogue-document half from 58 to 66 pages (29 to 33 catalogue documents). Versioned content (icvoss/django-brickwork#414) then moved it from 66 to 68 pages (33 to 34 catalogue documents), completing Documentation at 7 of 7. The Editorial family's article archetype (icvoss/django-brickwork#415) then moved the catalogue-document half from 68 to 70 pages (34 to 35 catalogue documents), opening Editorial at 1 of 7. The Editorial author archetype (icvoss/django-brickwork#416) then moved it from 70 to 72 pages (35 to 36 catalogue documents), Editorial at 2 of 7. The Editorial category archetype (icvoss/django-brickwork#417) then moved it from 72 to 74 pages (36 to 37 catalogue documents), Editorial at 3 of 7. The Editorial archive archetype (icvoss/django-brickwork#418) then moved it from 74 to 76 pages (37 to 38 catalogue documents), Editorial at 4 of 7. The Editorial series archetype (icvoss/django-brickwork#419) then moved it from 76 to 78 pages (38 to 39 catalogue documents), Editorial at 5 of 7. The Editorial related-content archetype (icvoss/django-brickwork#420) then moved it from 78 to 80 pages (39 to 40 catalogue documents), Editorial at 6 of 7. The Editorial reading-progress archetype (icvoss/django-brickwork#421) then moved it from 80 to 82 pages (40 to 41 catalogue documents), completing Editorial at 7 of 7. The Data-heavy operations data-empty/error archetype (icvoss/django-brickwork#407) then moved it from 82 to 84 pages (41 to 42 catalogue documents), completing Data-heavy operations at 7 of 7. The Product applications search and activity archetypes (icvoss/django-brickwork#404, #405) then moved it from 84 to 88 pages (42 to 44 catalogue documents), Product applications at 15 archetypes. The Marketing campaign, contact and conversion archetypes (icvoss/django-brickwork#400, #402, #403) then moved it from 88 to 94 pages (44 to 47 catalogue documents), completing Marketing and public web at 7 of 7. The Transactional enrolment, checkout, review, confirmation, receipt and status-tracking archetypes (icvoss/django-brickwork#422 to #427) then moved it from 94 to 106 pages (47 to 53 catalogue documents), completing Transactional journeys at 7 of 7. a11y/generate_archetype_fixtures.py discovers over every whole-document kind in its own _SCANNED_KINDS rather than over archetype alone, precisely so correcting a count cannot silently withdraw a surface from the axe, no-JS and keyboard gates |
| Logical properties | 273 in source CSS | 500 in compiled dist. Dated at 3.10.0. Reproducible: grep -oE '\b[a-z-]*(inline\|block)[a-z-]*\s*:' frontend/src/components.css \| grep -v 'display\s*:\|inline-block' \| wc -l gives 269 (declarations whose property name contains inline or block, excluding display: inline-block), plus grep -oE '(^\|[^-a-z])inset\s*:' frontend/src/components.css \| wc -l gives 4 (bare inset: shorthand declarations); 269 + 4 = 273. The prior 274 figure's methodology was never recorded (commit 8ee225d only logs the audit correction from 142 to 274, no rule); it is not reproducible from the recorded rule above, so the 1-count delta cannot be attributed honestly beyond "not the same method" |
| Version | 4.3.1 | consistent in pyproject.toml and src/brickwork/__init__.py. Gated |
| Hard runtime dependency | Django only | |
| Theme axes | 4 verified working | brand, theme, density, direction |
| Contract manifests | 3 | token, template, interaction; generated from source, CI drift-gated. Token manifest carries minContrast: 4.5 on fg-on-accent. Interaction manifest covers Alpine.data names, bw: events and package-owned HTMX target IDs (icvoss/django-brickwork#229). Gated |
6. Design boundaries
| Boundary | Statement |
|---|---|
| Django-admin | Not a Django-admin skin. It is for hand-built interfaces. |
| Design ownership | brickwork owns reusable interface design, including content, data and workflow patterns. Consumers own domain data, permissions, business rules and integrations. |
| Data visualisation | Brickwork owns the visual, layout, state and interaction contract. Consumers may supply a charting engine or specialist renderer that fits that contract. |
| Domain-specific rendering | A consumer owns product-unique semantics and business logic. Brickwork owns a reusable interface pattern whenever one exists. |
| Date entry | Field and panel chrome (_date_picker_chrome.html) ships. A package-maintained JavaScript calendar engine / bw_date_picker Alpine component does not (BR-BW-INPUT-004). A full date-range-picker example is available to copy and own. Brickwork owns the date-entry interface contract and may evolve its delivery model when a reusable engine is warranted. |
| Utility layer | No general Tailwind utility layer; brickwork ships no .grid, .gap-4, .px-3. |
| Page builder / CMS | Not a page builder, not a CMS. No tenant-arbitrary content or CSS/JS. |
| Sanitisation | Styles markup; does not sanitise it. |
| Auth state | Never reads auth state; state is host-injected. |
7. The bounded competitive picture
Research found no package combining app shell, components, theming and a marketing kit for server-rendered Django. This is absence of evidence within the searches run, not a certified negative: never phrase it as "the only" or "nothing else exists".
| Comparator | Why it is not the same category |
|---|---|
| django-unfold | Admin-only; not for hand-built application views |
| Flowbite, DaisyUI | Not Django-aware |
| django-crispy-forms | Forms only, no shell, no theming system |
| shadcn-django | Copy-paste, no shell, no upgrade path |
Those kits (and the wider panel in VISUAL-BAR.md) remain the visual meet-or-beat bar for craft and density. Category difference does not lower the visual bar; it only means they are not substitutes for a Django substrate.
A Django core contributor states on the Django Forum (thread 40718) that there is no agreed answer for Django UI component reuse. Cite this as context for the gap, not as brickwork's own claim to uniqueness.
8. Market timing
JetBrains/DSF Django Developer Survey 2025 (approximately 4,600 respondents), via the JetBrains blog summary: HTMX adoption among Django developers rose from 5% (2021) to 24% (2025); Alpine from 3% to 14%; React fell from 37% to 32%. JetBrains' own framing: "the pendulum has shifted back towards server-side templates." Attribute to the JetBrains blog summary; the figures are as reported there, not from the raw dataset.
9. Claim-honesty rules for anyone writing brickwork copy
- Never write "guarantee" about accessibility. State the mechanism (what runs, on how many documents, both themes, what else blocks) instead.
- Automated a11y testing has a ceiling. Cite Deque's ~57% figure as Deque's, as a stated limitation, never as brickwork's own measurement.
- Use the gate's own recorded miss (the 4.25:1 contrast defect that axe ran green over) as proof, not something to omit.
- Never claim a house aesthetic. See section 10; brickwork's contract is tokens that enable different-looking products, not a look of its own.
- Never make an absolute claim a reader can disprove in devtools. State the documented exception alongside any "zero" or "always" claim (background-position RTL exception, date-range-picker's scoped CSS).
- Bound every competitive claim. "No package found combining X" plus the search-scope hedge, never "only" or "nothing else exists".
- Never invent a URL, a customer, a testimonial, a statistic, or a
capability. If a fact is missing, write
[FACT NEEDED: ...]rather than filling it plausibly. - State RTL precisely. Structural proof (logical-property counts) exists;
an axe-tested
dir="rtl"fixture does not. - Never state a delivery-model boundary as an absence of capability.
brickwork declines to ship certain things as maintained components; that is
not the same as not providing them. "No date picker" is false and a
developer disproves it by opening one example; "no date picker engine"
(and "no
bw_date_pickerAlpine component") is true and explains the model. Field and panel chrome (_date_picker_chrome.html) is substrate, not an engine. Check every "we do not ship X" claim for this confusion before publishing it.
10. Anti-pattern: no house aesthetic (what killed the previous copy)
The brickworkui.com hero claimed brickwork "produces warm, composed, editorial-feeling products". Four independent review panels agreed this is a category error: it attributes a site-level aesthetic to the package. The warmth came from that site's own token override block, which any consumer replaces. The words "warm", "editorial" and "composed" appear nowhere in brickwork's positioning docs as brand voice.
Rule: brickwork's positioning may never claim a house aesthetic. The ratified position is strong contracts and tokens that enable different-looking products (ADR-054's three-layer model: substrate, base-theme, brand-theme), where brand themes are the consumer's delta. A claim that brickwork makes things look a particular way contradicts that model.
The same copy also claimed "0 bespoke CSS frameworks" while the site shipped 661 lines of site-authored CSS, disprovable in devtools in under a minute. Rule: never make an absolute claim a reader can disprove with one tool.
Note (not brand voice, do not "fix"): "editorial" appears once in the
codebase in a technical sense, distinguishing a callout component from an
application alert (src/brickwork/examples/sections/content/callout.html:5).
That is a UI-pattern term, unrelated to the rejected brand-voice claim above.
11. Required downstream fixes
These surfaces currently contradict this document and must be corrected to match it:
| Surface | Contradiction | Fix |
|---|---|---|
README.md |
Was stale on package version and the a11y fixture/document count | RESOLVED 2026-08-26: version updated to 3.10.0 and the a11y line restated in the current two-gate, 138-document framing (106 hand-maintained plus 32 archetype), consistent with section 5's A11y gate row |
pyproject.toml:8 |
Was "Accessible by construction" (a design claim), read against README.md:11-12's "a tested guarantee... not a claim" (a verification claim positioned explicitly against design claims) |
RESOLVED: pyproject.toml:8 now reads "WCAG 2.2 AA tested in CI", which is a verification claim and aligns with README.md's framing; no reconciliation remains outstanding |
PILOT-ADOPTION-BRIEF.md |
Was pinned to 0.3.0 and the private index, and dropped "professional" from the definition | RESOLVED 2026-08-24: rewritten as a routing quickstart and renamed docs/QUICKSTART.md |
Four documents each currently state brickwork's singular value differently: README.md:10-13 ("its value is the professional baseline"), docs/BRANDING.md:3-4 ("brickwork's whole point is that you rebrand it by overriding tokens"), docs/DESIGN.md:15-18 (the beautiful-defaults founding statement), pyproject.toml:8 (the category definition) |
Four different leads for one product | RESOLVED 2026-09-18: section 3 lead is "Beautiful by default" plus the customer outcome line. G1 demoted to support. Token-first rebranding (4.3) and tested accessibility (4.2) remain supporting claims. DESIGN.md "beautiful defaults" engineering language now aligns with the public lead |
| brickworkui.com homepage H1 and site copy-platform still lead with "Beautiful interfaces for anything." or G1 | Diverges from package POSITIONING at the installed pin | OPEN: site remaster icvoss/brickworkui.com#233 must align H1/meta to "Beautiful by default" and the customer outcome line |
Umbrella docs/marketing/brickwork/ messaging and fold concepts still quote prior leads |
Marketing strategy canon lags package POSITIONING | OPEN: follow-up on icvoss/icv-oss-umbrella (file with this PR) |
Every factual claim in this document carries its evidence inline. If a future edit adds a claim without a file:line, ADR number, or named external source next to it, that edit fails review.