Skip to content

feat: strict cssTypes - #123305

Merged
TkDodo merged 4 commits into
masterfrom
tkdodo/ref/re-implement-CSSProperties
Sep 2, 2026
Merged

feat: strict cssTypes#123305
TkDodo merged 4 commits into
masterfrom
tkdodo/ref/re-implement-CSSProperties

Conversation

@TkDodo

@TkDodo TkDodo commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

This PR adds stricter cssTypes implementations than React.CSSProperties, which has no type guarantees as everything is unioned with (string & {}), making it a type-hint at best.

In doing so, I found a couple of bugs where we pass wrong things (like unit-less numbers to width/height) that lead to ignored css styles.

@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

📊 Type Coverage Diff

Metric Before After Delta
Coverage 95.34% 95.34% ±0%
Typed 138,273 138,273 ±0
Untyped 6,760 6,758 🟢 -2
🔍 1 new type safety issue introduced

Type assertions (as) (1 new)

File Line Detail
static/app/stories/tokenReference.tsx 32 as T[keyof T]value as T[keyof T]

This is informational only and does not block the PR.

height="100%"
position="relative"
minHeight={typeof minHeight === 'number' ? `${minHeight}px` : minHeight}
minHeight={minHeight}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

runtime check is not necessary since our csstypes only allow strings except for 0 (which can be unitless)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

not claiming that this is complete, but I let AI come up with all the units and made sure it passed all our current usages. We can amend this as we see fit in the future.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is an OK start but I'd love if we aligned more with the values and units spec for the internal types.

@@ -0,0 +1,57 @@
type Globals = 'inherit' | 'initial' | 'revert' | 'revert-layer' | 'unset' | CssFunction;
type CssFunction = `${string}(${string})`;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

note:CssFunction is purposefully quite wide. It covers:

var(--something)
calc(something)
min(something, something)
minmax(...)

etc. We can narrow this if we want. One problem is that we can’t do:

`var(--${string})` | `${string}(${string})`

because then something like var(++foo) would still work because the second part of the union is wider than the first. So if we make this more narrow, we need an explicit list, which is why I didn’t do this in the first iteration.

<Container position="relative" className={className}>
<RightPaddedTextArea autosize rows={5} maxRows={10} {...props} />
<Container position="absolute" top="8px " right="10px">
<Container position="absolute" top="8px" right="10px">

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

wat !? I think the extra blank doesn’t matter at runtime but still ...

{Object.entries(props.tokens).map(([token, value]) => (
<Token key={token} scale={props.scale} {...{token, value}}>
{props.renderToken({value, token})}
{props.renderToken({value: value as T[keyof T], token})}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Object.entries sadly widens the type of value to string

Comment on lines -673 to 675
<Flex height={CHART_HEIGHT} justify="center" align="center">
<Flex justify="center" align="center">
<Placeholder height={`${CHART_HEIGHT}px`} />
</Flex>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this was a real bug. height needs a unit, but CHART_HEIGHT doesn’t have one because we pass it to echarts, which expects a number. Looks like this in the browser:

Image

now this didn’t have any impact, because the Placeholder has the correct height, which is why I removed it. But it now type-errored which is nice

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

@TkDodo

TkDodo commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e107f8a. Configure here.

@TkDodo
TkDodo marked this pull request as ready for review September 1, 2026 10:12
@TkDodo
TkDodo requested review from a team as code owners September 1, 2026 10:12

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Danny Trejo giving a chefs kiss gesture

@natemoo-re natemoo-re left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Very nice! Few suggestions on the module shape but overall really happy with this change and the bug catches!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is an OK start but I'd love if we aligned more with the values and units spec for the internal types.

*/
type BaseUnit = 'px' | 'em' | 'rem' | 'ex' | 'rex' | 'cap' | 'rcap' | 'ch' | 'rch' | '%';

type WidthUnit =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Technically there's no delineation between width/height units in the spec. Both are valid <length> units for any property. Was it intentional to narrow the available surface?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yes it was intentional. do we ever need to set the height to something like “viewport width” ?

Comment thread static/app/components/core/cssTypes.ts Outdated
Comment on lines +51 to +57
export type CssWidth = BaseSize | `${number}${WidthUnit}` | Globals;
export type CssMinWidth = CssWidth;
export type CssMaxWidth = CssWidth | 'none';
export type CssHeight = BaseSize | `${number}${HeightUnit}` | Globals;
export type CssMinHeight = CssHeight;
export type CssMaxHeight = CssHeight | 'none';
export type CssInset = `${number}${HeightUnit}` | Globals | BaseDimension;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thoughts on structuring this as CSS['width'], CSS['minWidth'], etc? IMO that feels like the more familiar and discoverable pattern.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done: 8415c8a

@TkDodo
TkDodo enabled auto-merge (squash) September 2, 2026 07:35
@TkDodo
TkDodo merged commit a63b1e9 into master Sep 2, 2026
79 checks passed
@TkDodo
TkDodo deleted the tkdodo/ref/re-implement-CSSProperties branch September 2, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants