Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort shorthand vs. longhand declarations — breaking change behind config #1700

Merged
merged 14 commits into from
Sep 30, 2024

Conversation

kylorhall-atlassian
Copy link
Collaborator

@kylorhall-atlassian kylorhall-atlassian commented Jul 22, 2024

What is this change?

Adds sorting of shorthand vs. longhand to ensure longhand deterministically will override shorthand.

⚠️ This could result in regressions as this will no longer work, so it's behind config, but this may mean this change should never go through (if we added border to this sorting):

const styles = css({ borderTop: '1px solid blue' });
const noBorderStyles = css({ border: 'none' });

export default ({ noBorder }) => <div
  css={[styles, noBorder && noBorderStyles]}
/>

Why are we making this change?

This is because quite commonly this code may be applied in either direction meaning sometimes font-weight: bold is applied and sometimes it's not, depending on the order that the stylesheet was generated in.

const styles = css({ font: '12px / 24px' });
const boldStyles = css({ fontWeight: 'bold' });

export default ({ isBold }) => <div
  css={[styles, isBold && boldStyles]}
/>

Example in a Storybook:
Screenshot 2024-07-22 at 3 47 41 PM

How are we making this change?

Where we already sort atomic styles, we now sort shorthand vs. longhand in that same area.


PR checklist

  • Validate this works consistently as it's unclear if this sortAtomicStyleSheet iterates over all declarations or just one by one
  • Added an applicable test suite
  • Expand the list of shorthand properties, right now this is just outline and font, there are many more!
  • Investigate if there's further tests to update
  • Update the documentation in website/ with this new config

Copy link

changeset-bot bot commented Jul 22, 2024

🦋 Changeset detected

Latest commit: 4ed15eb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
@compiled/babel-plugin-strip-runtime Minor
@compiled/parcel-optimizer Minor
@compiled/webpack-loader Minor
@compiled/react Minor
@compiled/utils Minor
@compiled/ssr-app Minor
@compiled/css Minor
@compiled/webpack-app Patch
@compiled/parcel-transformer Patch
@compiled/parcel-config Patch
@compiled/parcel-optimizer-test-app Patch
@compiled/parcel-transformer-test-app Patch
@compiled/parcel-transformer-test-compress-class-name-app Patch
@compiled/parcel-transformer-test-custom-resolve-app Patch
@compiled/parcel-transformer-test-custom-resolver-app Patch
@compiled/parcel-transformer-test-extract-app Patch
@compiled/babel-plugin Minor
@compiled/codemods Patch
@compiled/eslint-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

netlify bot commented Jul 22, 2024

Deploy Preview for compiled-css-in-js ready!

Name Link
🔨 Latest commit 4ed15eb
🔍 Latest deploy log https://app.netlify.com/sites/compiled-css-in-js/deploys/66f604772c06d90008203475
😎 Deploy Preview https://deploy-preview-1700--compiled-css-in-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@kylorhall-atlassian kylorhall-atlassian force-pushed the sort-shorthand-longhand-properties branch 2 times, most recently from a3a841d to bc029db Compare August 30, 2024 07:40
]}
</Style>
);

// WARNING: This is wrong, the `focus` border properties are different than without focus, borders would be indeterministic.
Copy link
Collaborator Author

@kylorhall-atlassian kylorhall-atlassian Aug 30, 2024

Choose a reason for hiding this comment

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

If this is required, all "bucket sorting" needs to be drastically rewritten to be nested.

Right now it's flat:

.abc { border: none }
.abc:link { border: none }
.abc:visited { border: none }
/* … */
.abc:active { border: none }
@media (/* … */) { .abc { border: none } }

But this doesn't support @media (…) being sorted internally or .abc:focus { … } being sorted against each other.

This would need deeper nested bucketing:

.abc { border: none }
.abc{ border-top: none }
.abc:link { border: none }
.abc:link { border-top: none }
@media (/* … */) {
  .abc { border: none }
  .abc { border-top: none }
  .abc:active { border: none }
  .abc:active { border-top: none }
}

And I'm not exactly sure if that's even feasible for @media queries either…

@dddlr dddlr force-pushed the sort-shorthand-longhand-properties branch from 89df158 to c9cae48 Compare September 23, 2024 03:51
@dddlr dddlr changed the title EXAMPLE: Sort shorthand vs. longhand declarations — breaking change behind config Sort shorthand vs. longhand declarations — breaking change behind config Sep 23, 2024
'border-block-end-style',
'border-block-end-width',
],
'border-color': [
Copy link
Collaborator Author

@kylorhall-atlassian kylorhall-atlassian Sep 23, 2024

Choose a reason for hiding this comment

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

Example: Should this sort above border-top as well, not just the children? I think this one is still required.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@dddlr I'm not decided enough on what "perfect" looks like for these, so I think it's fine to have some bugs for very rarely used properties…let's circle back to this and call it a gap unless it's obvious.

…in a way, these are "siblings", but to me it feels like there's an explicit order? MDN doesn't really provide anything and I think there's no explicit browser setting—it's a first-defined-basis:

But if this makes sense, in theory I think for all of border-color, border-style, border-width, this should be:

Suggested change
'border-color': [
'border-color': [
'border-top',
'border-block',
'border-inline',
'border-left',
'border-right',
'border-bottom',

Copy link
Collaborator

Choose a reason for hiding this comment

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

ah the order of properties in shorthandFor no longer matters - i should probably just reset that to alphabetical order to be honest ...

shorthandBuckets is now used in packages/css/src/plugins/sort-shorthand-declarations.ts (previously was just in packages/react/src/runtime/sheet.ts) to enforce that border-{color,style,width} comes before border-{top,right,bottom,left}

example https://github.com/atlassian-labs/compiled/pull/1700/files#diff-3b4ae519f265680a755d14f6de289c4d7507a936e4911e95b9b3889fa1ac579bR187

Screenshot 2024-09-27 at 10 22 54

let me know if this aligns with what you have in mind

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it, the test LGTM, that's what this aShorthandBucket - bShorthandBucket does 👌

packages/utils/src/shorthand.ts Show resolved Hide resolved
@dddlr
Copy link
Collaborator

dddlr commented Sep 25, 2024

More todos

  • Add changesets
  • Ensure sorting in runtime mode (extraction turned off) is adequately tested
  • Ensure sorting border* properties is deterministic when stylesheet extraction is turned on
  • Benchmark whether adding 14 extra buckets at runtime makes anything slower

@dddlr dddlr marked this pull request as draft September 25, 2024 05:01
kylorhall-atlassian and others added 6 commits September 25, 2024 16:18
…rting as well.

Concerns:
 - Runtime sorting does not work properly as we'd also need per-bucket nested sorting where `@media, :focus, border` needs to be sorted.
 - `shorthandFor` needs 40 more shorthand properties defined…
…uents.

This no longer sorts `border-top` vs. `border-block-start` as they are siblings, not a sibling of a constituent…
… by moving shorthandFor to @compiled/react/runtime
@dddlr dddlr force-pushed the sort-shorthand-longhand-properties branch from c948dbd to 9ca3547 Compare September 25, 2024 06:19
@dddlr dddlr marked this pull request as ready for review September 26, 2024 04:35
@dddlr
Copy link
Collaborator

dddlr commented Sep 26, 2024

docs deploy preview

https://deploy-preview-1700--compiled-css-in-js.netlify.app/docs/shorthand

based on my best understanding of how it works, feel free to make any corrections

Copy link
Collaborator

Choose a reason for hiding this comment

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

this appeared to be unused everywhere and was causing me grief when building

@@ -11,6 +11,8 @@ const classNameCompressionMap = require('./class-name-compression-map.json');

const extractCSS = process.env.EXTRACT_TO_CSS === 'true';

console.log('Stylesheet extraction enabled?', extractCSS);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this console.log intentional?

Copy link
Collaborator

Choose a reason for hiding this comment

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

oh yeah, i added it as a debugging measure but then decided to keep it as it seemed to be useful

happy to remove it if it's not valuable

liamqma
liamqma previously approved these changes Sep 26, 2024
Copy link
Collaborator Author

@kylorhall-atlassian kylorhall-atlassian left a comment

Choose a reason for hiding this comment

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

@dddlr This is my own PR so I can't request changes, but could use a sanity check in a few places and minor suggestions, nothing too blocking.

packages/react/src/runtime/__tests__/style.test.tsx Outdated Show resolved Hide resolved
packages/react/src/runtime/sheet.ts Outdated Show resolved Hide resolved
packages/react/src/runtime/shorthand.ts Show resolved Hide resolved
'border-block-end-style',
'border-block-end-width',
],
'border-color': [
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@dddlr I'm not decided enough on what "perfect" looks like for these, so I think it's fine to have some bugs for very rarely used properties…let's circle back to this and call it a gap unless it's obvious.

…in a way, these are "siblings", but to me it feels like there's an explicit order? MDN doesn't really provide anything and I think there's no explicit browser setting—it's a first-defined-basis:

But if this makes sense, in theory I think for all of border-color, border-style, border-width, this should be:

Suggested change
'border-color': [
'border-color': [
'border-top',
'border-block',
'border-inline',
'border-left',
'border-right',
'border-bottom',

packages/utils/src/shorthand.ts Show resolved Hide resolved
packages/utils/src/shorthand.ts Show resolved Hide resolved
transition: 1,
'view-timeline': 1,
} as const satisfies Record<ShorthandProperties, Depths>;
// ^^ this type lets us enforce that the copy of shorthandBuckets
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To question aloud, why do we even have this in this file?

…what if we imported this into the other file, doesn't that just work? I'd expect shorthandFor to be tree shaken, am I wrong?

It's fine, it feels maintainable, just looking at it again, I'm not sure why it's in both places…

Copy link
Collaborator

Choose a reason for hiding this comment

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

ah i made two copies of shorthandBuckets because in @compiled/react/runtime, importing @compiled/utils bloats the bundle size and causes npx size-limit to fail yeah

re: tree shaking, can we assume that tree-shaking always runs in realistic Compiled usage? if so, i can see if i can update the size-limit config to account for tree-shaking maybe

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have no clue 😸

website/packages/docs/src/pages/shorthand.mdx Show resolved Hide resolved
@JakeLane
Copy link
Collaborator

Can we add linting for this area?

This change makes the behaviour deterministic but it's still confusing to developers. Given the example in the description:

const styles = css({ font: '12px / 24px' });
const boldStyles = css({ fontWeight: 'bold' });

export default ({ isBold }) => <div
  css={[styles, isBold && boldStyles]}
/>

The inverse could happen:

const styles = css({ font: '12px / 24px' });
const boldStyles = css({ fontWeight: 'bold' });

export default ({ isFont }) => <div
  css={[boldStyles, isFont && styles]}
/>

which would have deterministic behaviour opposite to what the developer intended.

Generally, we should make the linting sort the code with autofixers (and warn when not possible) so that the source represents the production code.

@dddlr
Copy link
Collaborator

dddlr commented Sep 27, 2024

Can we add linting for this area?

This change makes the behaviour deterministic but it's still confusing to developers. Given the example in the description:

const styles = css({ font: '12px / 24px' });
const boldStyles = css({ fontWeight: 'bold' });

export default ({ isBold }) => <div
  css={[styles, isBold && boldStyles]}
/>

The inverse could happen:

const styles = css({ font: '12px / 24px' });
const boldStyles = css({ fontWeight: 'bold' });

export default ({ isFont }) => <div
  css={[boldStyles, isFont && styles]}
/>

which would have deterministic behaviour opposite to what the developer intended.

Generally, we should make the linting sort the code with autofixers (and warn when not possible) so that the source represents the production code.

yeah sure

@zesmith2 is working on sorting shorthand and longhand properties within the same css/styled/etc function call, but we can add checking arrays as a follow-up task :)

@dddlr dddlr merged commit 9a15e74 into master Sep 30, 2024
13 checks passed
@dddlr dddlr deleted the sort-shorthand-longhand-properties branch September 30, 2024 00:53
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.

4 participants