Skip to content

fix: classes that compiled to nothing, SSR column mismatches, and a CI guard - #6850

Open
tsahimatsliah wants to merge 10 commits into
mainfrom
dead-classes-hydration-dailydotdev
Open

tsahimatsliah wants to merge 10 commits into
mainfrom
dead-classes-hydration-dailydotdev

Conversation

@tsahimatsliah

@tsahimatsliah tsahimatsliah commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Why

The phone shell (#6767) broke desktop feed settings by splitting w-74 into tablet:w-74 w-full. w-74 had never existed in our Tailwind config, so a no-op became a desktop regression (#6840 fixed it). A sweep of the files the shell PRs touched found more classes that compile to nothing and two SSR mismatches. This PR fixes what the sweep found.

Changes

Alpha modifiers work on tokens

  • bg-accent-avocado-default/40 and friends compiled to nothing because the token colours were bare var(--theme-…) strings. withAlpha wraps the accent, text, status, surface, background and overlay tokens with Tailwind's <alpha-value> placeholder, so /NN works for an alpha on the opacity scale (16, 24, 32, 40, 50, 64) and /[0.35] for any other value. Every off-scale modifier in the repo is written in the bracket form now. The plain utilities compile to the same colour as before.
  • The custom caret plugin is removed: it wrote theme colours straight into caret-color and would have passed the placeholder through. Core Tailwind generates the same caret-* classes from theme.colors.

Dead classes

  • Spotlight: animate-spotlight-scrim-in, animate-spotlight-panel-in and animate-spotlight-list-fade were never defined (since feat: spotlight feature #6011). All three fade opacity only, so the both fill leaves no transform on the scrolling list or the panel.
  • The hot takes onboarding card's aura blobs and sparks, and the composer's spam warning, used alpha modifiers and never rendered; they do now (before/after: https://claude.ai/artifact/APm4A62j3TxZSK6rEz2cjz).
  • opacity-70 → opacity-64 (the scale has no 70), z-20 → z-[20] (keeps its place under the z-[21] overlay), line-clamp-10 added to the config.
  • Classes that never applied and only changed looks are deleted, since the shipped design is the one without them: shadow-1, scale-60, scale-80, two z-10s, the post analytics button's hover:bg-action-blueCheese-hober, the feedback caption's opacity-80, the squad brand button's hover:opacity-90.

Hydration

  • MainFeedLayout's --num-cards and the tag page's follow grid rendered the client's column count on the hydration pass while the server rendered one column (dev warned on /search at tablet width and on every tag page). In production React keeps a mismatched attribute, so the layout stayed one column until something re-rendered the component. FeedLayoutProvider now serves the default column count on the hydration render and the real one after mount, through useIsHydrated, which reads React's own hydration state via useSyncExternalStore (so a boundary hydrated after the first effects still gets the server value, and later mounts never flash). MainLayoutHeader drops its local hasHydrated state for it. numCards is not in any query key, so nothing refetches.

Verified

  • Every class in every touched file compiles through packages/webapp/tailwind.config.ts (checked with a one-off script, not part of this PR).
  • Lint clean in shared, webapp and extension. Webapp typecheck clean; shared and extension each have one pre-existing spec typing error this branch does not touch.
  • 518 shared suites (3,827 tests) and 110 webapp suites (850 tests) pass.
  • Hot takes onboarding card before/after at desktop light, desktop dark and phone dark, from the same Storybook story on main and the branch.

Events

No new event names.

Experiment

None.

🤖 Generated with Claude Code

Preview domain

https://dead-classes-hydration-dailydotd.preview.app.daily.dev

…I guard

The phone shell (#6767) broke desktop feed settings by splitting `w-74`
into `tablet:w-74 w-full`: `w-74` had never existed, so the no-op turned
into a desktop regression (#6840 fixed it). This sweeps the rest of the
classes that compile to nothing and guards against new ones.

Dead classes, now real:
- Spotlight: `animate-spotlight-scrim-in`, `-panel-in` and `-list-fade`
  were never defined; keyframes and animations added.
- `bg-<token>/NN` alpha modifiers cannot apply to CSS-variable colours
  (hot takes sparks and blobs, composer spam warning): `color-mix`.
- `opacity-70/80` -> `opacity-64`, `z-10` -> `z-1`, `z-20` -> `z-[20]`,
  `hover:opacity-90` on the brand button -> `hover:brightness-90`,
  `hover:bg-action-blueCheese-hober` -> `hover:bg-accent-blueCheese-flat`.
- `shadow-1`, `line-clamp-10`, `scale-60`, `scale-80` added to the config
  so the three places that already use them render.

Hydration:
- `MainFeedLayout`'s `--num-cards` and the tag page's follow grid rendered
  the client's column count on the hydration pass while the server
  rendered one column. In production React keeps a mismatched attribute,
  so the layout stayed one column until a later re-render. `useIsHydrated`
  renders the server value on that pass and the real one after mount;
  later mounts start hydrated, so client navigation does not flash.

CI:
- `scripts/check-dead-classes.mjs` compiles every class added by a branch
  through the real Tailwind config and fails on tokens with no selector.
  Runs as `dead_classes_changed` next to the strict typecheck.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
daily-webapp Ready Ready Preview Oct 12, 2026 12:50pm UTC

Request Review

- Alpha modifiers work on the accent, text and status tokens
  (`withAlpha` wraps each variable with Tailwind's `<alpha-value>`), so
  the hot takes and composer classes are written as `bg-x/40` or, for an
  alpha outside the opacity scale, `bg-x/[0.35]`, not color-mix literals.
- The three classes that never applied are deleted rather than replaced:
  the analytics button's hover (its Secondary pressed fill was the shipped
  look), the feedback caption's opacity (contrast), and the brand button's
  hover.
- `useIsHydrated` reads React's own hydration state through
  `useSyncExternalStore`, so a boundary hydrated after the first effects
  still renders the server value; `MainLayoutHeader` uses it too.
- The spotlight animations fade opacity only, so `both` leaves no transform
  on the scrolling list or the panel.
- The checker skips tokens that touch an interpolation, scans whole files
  and keeps the literals that reach an added line, and follows the repo's
  Prettier config.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@tsahimatsliah tsahimatsliah left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff, compiled the Tailwind config on main and on this branch, and read the failing CI logs.

Blocking

  1. CI is red. lint_shared and the lint step in test_webapp fail with tailwindcss/classnames-order in files this PR doesn't touch: PostFocusCard.tsx:794, SidebarDesktopV2.tsx:2544, Spotlight.tsx:1016 and :1103, and DevCardStep2.tsx:249. The cause is that registering shadow-1, scale-60/80 and animate-spotlight-* as real utilities changes where the sorter puts them. Running eslint --fix on those five files should fix it.
  2. caret-text-link breaks on every text field (inline comment on withAlpha.ts).

Non-blocking

  • The SSR column fix covers two consumers, but FeedContainer writes the same --num-cards without the gate (inline).
  • 6 existing /10, /20 and /30 usages still compile to nothing, and the diff-only guard won't flag them (inline).
  • eslint-plugin-tailwindcss is already installed, and its no-custom-classname rule covers what the new script does (inline).
  • Classes that never applied before now render, so several surfaces change visually. Before/after screenshots are needed (inline).
  • Scope: this PR combines a change to every text-*, accent-* and status-* utility (each now compiles to color-mix), a new hydration hook, a new CI tool and several visual changes. Splitting the colour-token change and the CI guard into their own PRs would make each one cheaper to review and to revert.

Reviewed by AI.

Comment thread packages/shared/tailwind/colors/withAlpha.ts
Comment thread packages/shared/src/components/MainFeedLayout.tsx Outdated
Comment thread packages/shared/tailwind/colors/status.ts
Comment thread scripts/check-dead-classes.mjs Outdated
Comment thread packages/shared/tailwind/boxShadow.ts Outdated
@tsahimatsliah

tsahimatsliah commented Oct 10, 2026 •

Copy link
Copy Markdown
Member Author

Review follow-up (c094692)

Blocking

  1. CI: the five files the new utilities re-sorted are fixed (eslint --fix); lint_shared, the webapp lint step and the extension lint are clean locally.
  2. caret-text-link: the custom caret plugin is removed; core Tailwind generates the same caret-* classes and resolves <alpha-value>. No other plugin reads the colour objects, and theme() in CSS substitutes 1.

Non-blocking

  • FeedContainer and the other numCards readers: the hydration gate moved into FeedLayoutProvider, so every consumer gets the server value on the hydration render and the real one after mount. The per-site gates are gone.
  • The 6 remaining alpha modifiers: withAlpha now covers accent, text, status, surface, background and overlay, and every off-scale alpha in the repo is written as /[0.1]-style (23 usages, 12 rewritten). All compile.
  • no-custom-classname: measured at 211 hits today, about 50 of them dead utilities and the rest hook classes and classes from embedded CSS that need a ~70-entry whitelist; with --max-warnings 0 even warn fails CI. I will open that as its own PR after this one, and it can retire the script then. Details in the thread.
  • Screenshots: shadow-1, scale-60/80 and the z-1s are deleted instead of made real. The one surface that now shows something new is the hot takes onboarding card (aura and sparks); before/after at three viewports: https://claude.ai/artifact/APm4A62j3TxZSK6rEz2cjz
  • Scope: the colour-token change stays here because the hot takes classes depend on it, and the guard is now one script plus one job. The dead-utility cleanup and the lint-rule switch are the follow-up PR.

Verified locally on c094692: lint clean in shared, webapp and extension; webapp typecheck clean (shared and extension each have one pre-existing spec typing error this branch does not touch); 518 shared suites (3,827 tests) and 110 webapp suites (850 tests) pass.

… hydration gate

- The custom `caret` plugin wrote theme colours straight into `caret-color`
  and passed `<alpha-value>` through; core Tailwind generates the same
  `caret-*` classes from `theme.colors` and resolves the placeholder, so
  the plugin is gone.
- `withAlpha` also wraps the surface, background and overlay tokens, and
  every alpha modifier in the repo that is off the opacity scale is an
  arbitrary alpha (`bg-x/[0.1]`), so no `/NN` on a token compiles to
  nothing any more.
- The hydration gate lives in `FeedLayoutProvider`: it serves the default
  column count on the hydration render and the real one after mount, which
  covers `FeedContainer`, `Feed`, `useFeed` and the two call sites that had
  their own gate.
- `shadow-1`, `scale-60`, `scale-80` and the two `z-1`s only changed looks
  and were never designed in; the classes are deleted instead of made real.
- Class order fixed in the files the new utilities re-sorted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@tsahimatsliah

Copy link
Copy Markdown
Member Author

CI is green on 0b0c4c7. The one failure after the last round was typecheck_strict_changed on pages/recruiter/[opportunityId]/plans.tsx, which this branch touched only to write bg-status-warning/[0.1]; its three strict errors are on unrelated, pre-existing lines, so the file is on the strict skip list with the usual note, as the script does for other touched-only files.

Comment thread .circleci/config.yml Outdated
tsahimatsliah and others added 2 commits October 12, 2026 15:22
It guards future PRs rather than fixing anything here; it will be
proposed separately, folded into the no-custom-classname lint rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 9f19696b Deployed Oct 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants