Skip to content

Provide an option to disable CSS scoping or auto-generated class names in components #15613

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
algorm opened this issue Mar 26, 2025 · 1 comment
Labels
css Stuff related to Svelte's built-in CSS handling needs discussion

Comments

@algorm
Copy link

algorm commented Mar 26, 2025

Describe the problem

With @layer baseline widely available, it is easy enough to write desired styles in vanilla CSS without the need to fight against specificity and writing order issues.
I prefer writing my styles using CSS cascade layers and don't feel the need for Svelte's CSS scoping feature. I also don't like my HTML elements getting unnecessary auto-generated cryptic class names attached to them.

This has been brought to attention in past also but no activity there for long time.

Describe the proposed solution

As Svelte always prefers to embrace native web technologies over custom solutions wherever possible, there should at-least be an option in svelte.config.js to disable CSS scoping application wide for the users who don't want this nice to have feature.

Thanks for awesome work.

Importance

would make my life easier

@algorm algorm changed the title Provide an option to disable CSS scoping and auto-generated class names in components Provide an option to disable CSS scoping or auto-generated class names in components Mar 26, 2025
@NicolaFriuli
Copy link

NicolaFriuli commented Mar 27, 2025

Although I disagree with using a global CSS file to style all components of an application, I strongly believe that a rework on how scoped classes are being applied should happen as it is practically impossible to style an imported component unless it's used as a child of another element.

The Problem

Let's suppose we have a general Text.svelte component:

<script>
  let { tag = "div", class: className, children, ...props } = $props();
</script>

<svelte:element this={tag} class={["text", className]} {...props}>
  {@render children?.()}
</svelte:element>

<style>
  .text {
    color: var(--color-50);
    line-height: 1.65em;
    font-weight: 300;
    font-size: 1rem;
    font-stretch: 120%;
  }
</style>

Svelte will now apply the .text class in addition to the generated hash of the Text.svelte component: class="text s-yPsWO7AWW1Qv".

If we then decide to extend the Text.svelte component, which by the way is a common design pattern, creating a Heading.svelte component, we'll run into some styling issues:

<script>
  import { Text } from "$lib/components/Text";

  let { tag = "h2", fill = "gradient", class: className, children, ...props } = $props();
</script>

<Text {tag} class={["heading", className]} data-fill={fill} {...props}>
  {@render children?.()}
</Text>

<style>
  .heading {
    font-weight: 600;
    font-stretch: 151%;
    text-wrap: balance;

    &[data-fill="solid"] {
      color: var(--color-50);
    }

    &[data-fill="gradient"] {
      color: transparent;
      background: linear-gradient(180deg, var(--color-50) 50%, var(--color-300) 80%);
      background-clip: text;
      -webkit-background-clip: text;
    }
  }
</style>

Svelte will now apply the .text and .heading classes in addition to the generated hash of the Text.svelte component: class="text heading s-yPsWO7AWW1Qv". This means that the .heading style rule, that we defined inside of the Heading.svelte component, will simply not work as the heading's component hash is missing.

What if we tried using the :global() directive?

Well, it simply won't solve the issue:

<style>
  :global(.heading) {
    font-weight: 600;
    font-stretch: 151%;
    text-wrap: balance;

    &:global([data-fill="solid"]) {
      color: var(--color-50);
    }

    &:global([data-fill="gradient"]) {
      color: transparent;
      background: linear-gradient(180deg, var(--color-50) 50%, var(--color-300) 80%);
      background-clip: text;
      -webkit-background-clip: text;
    }
  }
</style>

This time Svelte will still apply the .text and .heading classes in addition to the generated hash of the Text.svelte component: class="text heading s-yPsWO7AWW1Qv". However the .heading class will have less specificity compared to .text.s-yPsWO7AWW1Qv which means that the browser will prioritise .text on all the properties that are defined in both rules (color, font-weight, font-stretch).

The Solution

Just take a look at what the competition is doing:
Next.js, when using CSS modules, applies both hashed classes to our Heading component: class="Text_text__GFahY Heading_heading__o6CE3". This means that the .heading rule overrides the .text rule and the Heading component will now be rendered as expected.

@dummdidumm dummdidumm added css Stuff related to Svelte's built-in CSS handling needs discussion labels Mar 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css Stuff related to Svelte's built-in CSS handling needs discussion
Projects
None yet
Development

No branches or pull requests

3 participants