Skip to content

docs(types,components): name the real per-tier page-source styling primitive - #5471

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-5461-page-source-tailwind-framing
Aug 21, 2026
Merged

docs(types,components): name the real per-tier page-source styling primitive#5471
os-support-ai merged 1 commit into
mainfrom
claude/issue-5461-page-source-tailwind-framing

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#5461

Three sources still taught the "constrained JSX/HTML + Tailwind" framing that ADR-0080's
2026-06-30 header amendment retracted on styling. One of them ships in a published .d.ts
and links the very guide that now says the opposite.

The authority I matched against — not this PR's own phrasing

Two landed sources, cross-checked against each other before a word was written:

  1. content/docs/guide/react-pages.md §"Styling — page source is metadata, not build
    input", corrected by docs(guide): react-pages.md contradicts the framework's sources on four points — Tailwind styling, record:* scope, adapter.find options, and the block inventory #5413 / PR docs(guide): resolve four react-pages.md contradictions toward the framework's sources #5462 and on main as of 688cb93ad. It carries the
    per-tier table verbatim:

    kindStyle with
    "react"Inline style={{ … }}, with hsl(var(--token)) for colour.
    "html"The blocks' own structured props (flex direction/gap, grid columns) plus a JSON style object.
  2. packages/lint/src/validate-page-source-styling.ts in the framework —
    validatePageSourceStyling, rule id page-source-className-tailwind, released in
    @objectstack/lint@11.5.0. Read directly, not from the card's summary. It gates on
    kind !== 'html' && kind !== 'react' && kind !== 'jsx' → continue, emits
    severity: 'warning', and its two hints are the same split as the guide's table:
    react → inline style={{}} with hsl(var(--token)) theme colors; html → the
    components' structured props plus a JSON style object.

Both trace to ADR-0080's header amendment (2026-06-30, under ADR-0065, Accepted),
which I read at source: "The tiers themselves stand; only the styling primitive
changes ... Do not author Tailwind classes in page source."

Citation hygiene, per the card's own warning. The card flagged that this family's
citation trail had a bad link elsewhere — validate-responsive-styles.ts does not read
page source at all. Verified: that rule walks regions[].components[], so it is not
cited here. Two further corrections to the trail:

  • objectstack PR #10436 is still an open draft, not landed. The card and its dependency
    note both say the spec-side PageSchema describes "has just" been fixed. As of
    objectstack@7679f8b54 (2026-08-21 00:13) packages/spec/src/ui/page.zod.ts:520/560/566
    still carry constrained JSX/HTML+Tailwind. This changes nothing about the defect here —
    the ADR amendment, the released lint rule and the corrected guide are all landed and all
    agree — but the wording in this PR is aligned with that pending PR's shape, so the two
    trees converge rather than diverge when it merges.
  • The three-site count is wrong. See the sweep.

The change

Comment/TSDoc prose only. No behaviour change, no export change, the accepted kind set
untouched.

packages/types/src/layout.ts — the TSDoc on PageSchema.kind. This is the
consumer-facing one: it ships in the built .d.ts and is what shows on hover. Verified,
not assumed — after pnpm --filter @object-ui/types build, the new prose is at
packages/types/dist/layout.d.ts:582-604. It now states the per-tier primitive, that
colors on both tiers come from the theme as hsl(var(--token)), and the do-not-author
rule with its mechanism and the rule id that reports it.

packages/components/src/renderers/layout/react-page.tsx — the injected-scope note
(:24) and the buildComponentScope comment (:49), plus the styling note the file
header was missing (inline style objects with token colours; overlays through
ObjectForm with formType="drawer"|"modal" rather than a hand-rolled fixed inset-0).

Every load-bearing claim survives, which was the card's explicit constraint:

ClaimSurvives
html is PARSED into a SchemaNode tree, never executed✅ verbatim
html is safe for untrusted authors✅ verbatim
'jsx' is a deprecated alias, still accepted✅ verbatim
react is EVALUATED in the main tree, no sandbox✅ verbatim
react gated behind the react-pages host capability✅ verbatim
ADR-0080 citation + the link to react-pages.md✅ verbatim
"layout containers are deliberately not injected"✅ verbatim — only the "so use Tailwind" conclusion is replaced

content/docs/guide/react-pages.md is not touched: it is already correct as of PR #5462.

Sweep — the card's "three" is not the complete set

The card measured with grep -rn "HTML + Tailwind" packages/ apps/ content/ → 3 hits. That
single-line, single-spelling probe misses three classes: the no-space spelling
HTML+Tailwind, the JSX/Tailwind spelling that drops "HTML", and any occurrence wrapped
across a line boundary. A multiline-tolerant sweep for every compound spelling, run at the
merge base 9bd753682:

grep -rnP -U -z -o --include='*.ts' --include='*.tsx' --include='*.md' --include='*.mdx' \
--include='*.json' --include='*.js' --include='*.mjs' --include='*.css' \
'(JSX|HTML|html|jsx)\s*[/+]\s*(\*|>|//|\n|\r|\s)*\s*Tailwind' .

11 live sites, not 3 (plus 5 in CHANGELOG.md files — immutable release history, left
alone). Three are this PR's; the other 8 are outside the declared file surface, so per the
dispatch's stop-on-breach clause they are filed, not fixed:

Counter-probe

The sweep's zeros are real zeros, not a broken search — each probed with the identical
grep invocation:

Claimed zeroControl phrase, same grep -rnP -U -z -oHits
no compound framing in scripts/Tailwind19
no compound framing in skills/Tailwind39
no compound framing in docs/Tailwind2
no compound framing in examples/Tailwind10
no compound framing in e2e/Tailwind returns 0 there too, so probed with expect290
post-fix: no compound framing left in this PR's two filesTailwind in those same two files7

The last row is the one that matters for this PR: the compound spelling is gone from both
files (0 hits) while the same command still finds 7 bare Tailwind mentions in them, so
the zero is the edit's, not the probe's.

Verification

All commands below on the final commit 7d2391fde, exit codes captured before any pipe.

pnpm --filter @object-ui/types build BUILD_EXIT=0
pnpm --filter '@object-ui/components^...' build DEPBUILD_EXIT=0 (dependency closure first)
pnpm exec vitest run packages/components/ TEST_EXIT=0
Test Files 171 passed (171)
Tests 1560 passed (1560)
pnpm --filter @object-ui/types --filter @object-ui/components type-check
packages/types type-check: Done (tsc --noEmit && tsconfig.examples.json && tsconfig.test.json)
packages/components type-check: Done (tsc --noEmit && tsconfig.test.json)
TC_EXIT=0

The test run is from the repo root with no --, per this repo's vitest guard (objectui#3288 /
#3378) — the package-scoped pnpm --filter … test -- --maxWorkers=2 form is refused here,
and the refusal is correct.

Gates re-derived from the actual diff and re-run on the final commit:

check-control-bytes.mjs EXIT=0
check-changeset-presence.mjs EXIT=0
check-changeset-no-major.mjs EXIT=0
check-changeset-fixed.mjs EXIT=0
eslint packages/types/src/layout.ts EXIT=0
eslint packages/components/src/renderers/layout/react-page.tsx EXIT=0 (6 pre-existing `any` warnings, all on untouched lines)

Control-byte self-scan beyond the gate, on all three changed files:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' → no match.

Changeset

One entry, @object-ui/typespatch — consumer-visible, and evidenced rather than
asserted: the new TSDoc is present in the built packages/types/dist/layout.d.ts, which is
what a consumer's editor reads on hover. Scored patch, never major (AGENTS.md §版本号策略).

The react-page.tsx half gets no entry of its own: file-header and internal function
comments project into no .d.ts and change no export, so there is nothing an
@object-ui/components consumer could read in a CHANGELOG and act on. It is named in the
changeset body rather than left silent.


Generated by Claude Code

…imitive (#5461)
Three sources still taught the "constrained JSX/HTML + Tailwind" framing that
ADR-0080's 2026-06-30 header amendment (under ADR-0065, Accepted) retracted on
styling: a page's `source` is runtime metadata, the console's Tailwind is compiled
at build time over the console's own `src` with no safelist, so an authored utility
class silently produces no CSS.
- `packages/types/src/layout.ts` — the TSDoc on `PageSchema.kind`. Published
surface: it ships in the built `.d.ts` and shows on hover, and it links
`content/docs/guide/react-pages.md`, which #5413 has already corrected — so the
shipped type documentation contradicted the guide it points readers to.
- `packages/components/src/renderers/layout/react-page.tsx` — the injected-scope
note and the `buildComponentScope` comment, plus the styling note the file
header was missing.
Wording is matched to the two landed authorities, not invented: the corrected
guide's per-tier table, and `validatePageSourceStyling` (rule
`page-source-className-tailwind`, `@objectstack/lint@11.5.0`). The "layout
containers are deliberately not injected" claim is untouched — only the "so use
Tailwind" conclusion is replaced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)25.3 KB350 KB
Entry fileindex-WY6gyyO9.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)10.04KB3.72KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)29.34KB7.05KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)6.35KB2.43KB
auth (index.js)2.77KB1.22KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.89KB
auth (useIsWorkspaceAdmin.js)3.04KB1.45KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)507.00KB113.63KB
core (index.js)4.11KB1.62KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)159.80KB44.34KB
fields (index.js)237.21KB59.50KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.22KB3.08KB
i18n (provider.js)23.13KB7.63KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)30.51KB7.57KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.35KB3.31KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.42KB1.42KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.81KB0.83KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.75KB18.37KB
plugin-chatbot (index.js)181.21KB43.14KB
plugin-dashboard (index.js)128.37KB32.91KB
plugin-designer (index.js)212.39KB42.83KB
plugin-detail (index.js)242.04KB60.86KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)124.40KB30.26KB
plugin-gantt (index.js)164.10KB39.87KB
plugin-grid (index.js)200.75KB54.24KB
plugin-kanban (index.js)52.93KB14.60KB
plugin-list (index.js)111.64KB27.13KB
plugin-map (index.js)20.08KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.49KB11.93KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.52KB20.67KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)36.10KB12.26KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.33KB0.69KB
react (schema-input.js)1.45KB0.83KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (index.js)4.77KB2.16KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)10.76KB3.17KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)6.92KB2.40KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.08KB1.53KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-support-ai@claude