Skip to content

[css-cascade-7] What happens with duplicate @sheet identifiers? #12001

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

Open
KurtCattiSchmidt opened this issue Mar 25, 2025 · 7 comments
Open

Comments

@KurtCattiSchmidt
Copy link

With the following CSS, what should happen?

@sheet sheet1 {
  * { font-family: serif; }
}
@sheet sheet1 {
  * { font-family: sans-serif; }
}

I can think of a few options:

  1. Last definition wins - this aligns with how CSS generally works and seems straightforward for developers.
  2. Rules with the same identifier are merged, like with duplicate @layer names. @layer is a bit different though, because source order of the name determines layer precedence, which isn't the case for @sheet.
@romainmenke
Copy link
Member

romainmenke commented Mar 25, 2025

I think this depends on if @sheet is more like a condition (e.g. @media, @layer, ...), or more like a name-defining at-rule (e.g. @keyframes, @font-face, ...).

I think the utility of the append or merge behavior is fairly limited and might be surprising to CSS authors when the sheets contain @import statements.

@sheet sheet1 {
  @import "foo.css";

  * { font-family: serif; }
}

@sheet sheet1 {
  @import "bar.css"; /* won't work, it appears after a style rule when merged */

  * { font-family: sans-serif; }
}

While "last one wins" can also be used by CSS authors to override definitions of sheets.

@sheet sheet1 {
  * { font-family: sans-serif; }
}

@media print { /* If nesting in conditions is even allowed? */
  @sheet sheet1 {
    * { font-family: serif; }
  }
}

@kbabbitt
Copy link
Collaborator

Agree with @romainmenke. I think @sheet falls into the "name-defining" category and so I would expect "last one wins" behavior.

@astearns astearns moved this to FTF agenda items in CSSWG April 2025 meeting agenda Mar 27, 2025
@astearns astearns moved this from FTF agenda items to By Topic in CSSWG April 2025 meeting agenda Mar 27, 2025
@astearns astearns moved this from By Topic to Wednesday Morning in CSSWG April 2025 meeting agenda Mar 27, 2025
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-cascade-7] What happens with duplicate @sheet identifiers?, and agreed to the following:

  • RESOLVED: WHere @sheet names conflict, the last one delcared wins
The full IRC log of that discussion <emeyer> kurt: Yet another @layer divergence, which I used as imspiration for @sheets
<emeyer> …In @layer, duplicate names cause rules to be merged, but here this is up for interpretation
<TabAtkins> q+
<emeyer> …Other name-defining rules are, last one wins, and I think that makes sense here
<emeyer> TabAtkins: I agree with the points in the thread
<astearns> ack TabAtkins
<emeyer> …@layer is setting an aspect of specificity, and not an independent construct
<emeyer> …@sheet does create a construct, and thus last shoudl win
<emeyer> astearns: Is that okay with you?
<emeyer> kurt: Yes
<bramus> (I’m undecided on it)
<emeyer> astearns: So @sheet conflicts go with last-defined wins
<emeyer> bramus: I’m just not sure which way this should swing, but Tab makes a good point
<emeyer> astearns: So your concern is more why @layer does this?
<emeyer> bramus: More that if @layer does this, why doesn’t @sheet? Authors might not see the technical different underneath the two things
<emeyer> astearns: How about a resolution to have @sheet use last-definition-wins for now, without prejudice; we can raise again later if the need arises
<emeyer> …Any objections?
<emeyer> (silence)
<kurt> https://github.com//pull/11980
<emeyer> RESOLVED: WHere @sheet names conflict, the last one delcared wins
<emeyer> kurt: Feedback welcome on #11980 as well
<kurt> Formatted version here: https://kurtcattischmidt.github.io/kurtspublishedw3cdrafts/AtSheet.html

@justinfagnani
Copy link

justinfagnani commented Apr 25, 2025

Sheet names should not form a global namespace. That's makes individual CSS files potentially interfere with each other. This would break the bundling use case and isolation between CSS modules.

Can you clarify if the last-rule wins rule applies across files?

@romainmenke
Copy link
Member

How would this break the bundling use case?
Can you provide a specific example?

That's makes individual CSS files potentially interfere with each other.

What you are describing is just how CSS works, right?

For my purposes (as maintainer of multiple CSS bundlers) it is actually important that this works exactly as any other CSS feature and that sheet names are shared across multiple files.

Multiple CSS files bundled with @sheet should work exactly the same as when importing each file individually. @sheet should not be an opt-in to some other way of applying multiple stylesheets to a document in my opinion.

Not saying that there isn't a use case for the behavior you are describing, but I am unsure if there is a strong enough case to go against so much precedent of how things work in CSS :)

@justinfagnani
Copy link

I guess I'm unclear on the status of the previously proposed global namespace of sheet names, where a sheet could be imported by name regardless of which outer stylesheet it came from.

What I think is important to preserve is that two separate stylesheets nested sheet names are not merged, ie:

Given:

a.css:

@sheet foo {
  p { color: red; }
}

b.css:

@sheet foo {
  p { font-weight: bold; }
}

This applies styles from both foo @sheet rules:

@import foo from "a.css";
@import foo from "b.css";

(ie, bold and red, not just bold);

And these two JS modules get their respective foo sheets independently:

/* a.js */
import {foo} from './a.css';
/* b.js */
import {foo} from './b.css';

A previous version of this proposal at least put sheet names into a global list and there was an open question of how to merge sheets within that list. Both merging and list-wins in that context would cause severe problems.

@romainmenke
Copy link
Member

This does indeed surface a nuance.

I don't think the intention is that first each stylesheet is imported and applied fully and only in a second pass that the specific @sheet's are picked.

If that is the issue you are referring to?

Instead each import statement that imports a specific @sheet only applies the contents of that @sheet rule.

The result should indeed be bold and red.


@import foo from "a.css";
@import foo from "b.css";

As foo is local to each @import statement there isn't any conflict between the two foo names sheets. Or at least that is my understanding :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Thursday Morning
Development

No branches or pull requests

5 participants