jrh
jrh

Reputation: 4193

How can I style a component whose class changes based on a `use` directive in Svelte?

I am creating a directive which adjusts the class of the element that it is applied to. However, the styles for that class do not apply when the class changes. For instance:

Form.svelte

<form id='sign-in' use:delayed={ handleSubmit }>
  <label for='sign-in-name'>Your Name</label>
  <input required id='sign-in-name' type='text' />

  <input type='submit' value='Sign In' />
</form>

<style>
  form {
    display: block;
  }

  form.submitting {
    display: none;
  }
</style>

Delayed.js

export default function(node, action) {
  node.addEventListener('submit', async function(event) {
    event.preventDefault()
    const originalClass = node.className

    node.className = `${ originalClass } submitting`
    await action()
    node.className = originalClass
  })
}

In this case, the class will change successfully in the DOM, but the form will still display. The form.submitting style doesn't even make it into the CSS generated by Svelte.

I know that I can work around this using a global stylesheet, but I'm curious why the scoped styles don't apply and if there's a way to make it work that way.

This works, but it feels hacky.

<style>
  form {
    display: block;
  }

  :global(form.submitting) {
    display: none;
  }
</style>

Upvotes: 0

Views: 1984

Answers (1)

rixo
rixo

Reputation: 25041

Svelte compiler removes unused CSS rules, that is rules with selectors that do not match in the markup of the component. You should have a compiler warning "Unused CSS selector" about that. And since the compiler can't see dynamically added classes, your form.submitting selector is removed.

The solution is indeed to make your dynamic selector :global(...).

If you want your style to only apply in the scope of this component and its children, you need a wrapping element that you can reference like such:

<div>
  <form>...</form>
</div>

<style>
  div :global(form.submitting) { ... }
</style>

Svelte will scope the div part of the selector to the current component, effectively meaning that the :global(...) part will only apply to the form inside a <div> inside this component.

Upvotes: 5

Related Questions