Skip to content

Svelte ​

@ecss/svelte-adapter generates typed Svelte components (4 and 5) from @block and @element declarations. The adapter's id is 'svelte'.

Unlike the React/Vue adapters, the Svelte adapter is module-based: it generates real .svelte files into the .ecss/svelte/ directory, and imports of ./X.ecss are redirected to them automatically by the ECSS plugin. This is transparent to you — the component is imported from the .ecss file as usual.

Installation ​

sh
npm i -D @ecss/svelte-adapter
sh
pnpm add -D @ecss/svelte-adapter
sh
yarn add -D @ecss/svelte-adapter

Requires svelte version 4 or higher to be installed.

Setup ​

The adapter is registered in ecss.config.ts via defineConfig from @ecss/config — the config is not tied to any particular bundler; it is read by the ECSS plugin of your bundler. defaultAdapter sets the adapter applied to .ecss files by default:

ts
// ecss.config.ts
import { defineConfig } from '@ecss/config';
import { svelteAdapter } from '@ecss/svelte-adapter';

export default defineConfig({
  adapters: [svelteAdapter()],
  defaultAdapter: 'svelte',
});

Usage ​

Each @block turns into a Svelte component that is imported directly from the .ecss file. The component name is formed as {prefix}{BlockName} (the default prefix is E): @block Button → EButton. Content is passed through the default slot.

svelte
<script lang="ts">
  import { EButton } from './Button.ecss';
</script>

<EButton as="button" params={{ variant: 'primary' }} onclick={onClick}>
  Click me
</EButton>

The params prop ​

The block's parameters are passed through a single params prop. Their types come from the generated {Block}Params interface (for example ButtonParams), so you cannot pass a non-existent @enum value.

params is required if the block has at least one required @param (without ?); if all parameters are optional or there are none at all, the prop can be omitted.

svelte
<EButton params={{ variant: 'primary' }} /> <!-- variant is required -->
<ECard />                                    <!-- Card has all parameters optional -->

The as prop ​

By default the component renders as a <div>. The as prop accepts any HTML tag and changes the root element. In Svelte 5 the type narrows by the chosen tag (via generics + SvelteHTMLElements), so the corresponding attributes are available:

svelte
<EButton as="button" type="submit" params={{ variant: 'primary' }}>Submit</EButton>

<EButton as="a" href="/about" params={{ variant: 'ghost' }}>Link</EButton>

With as="a", href, target, and other <a> attributes are available; with as="button", type, disabled, and so on.

class, style, and attributes ​

ECSS sets the block's class, the CSS variables from params, and data attributes on the root element. What the consumer passes is merged with these values:

  • class — the consumer's class is added to the block's class without overriding it;
  • style — added to the CSS variables generated from params;
  • the remaining attributes and handlers (id, aria-*, onclick, href, …) are forwarded to the root element and win on collision with data attributes.

Sub-components (@element) ​

If a @block contains @element, they become nested components through static properties of the root and are available in the template via dot notation:

svelte
<EButton params={{ withIcon: true }}>
  <EButton.Icon>
    <svg><!-- … --></svg>
  </EButton.Icon>
  <EButton.Text>Click me</EButton.Text>
</EButton>

Sub-components support the same as prop (the default is <div>) but do not accept params.

Options ​

The factory's only option is componentNamePrefix (the component name prefix, default 'E'): svelteAdapter({ componentNamePrefix: 'My' }) → MyButton. For more details, see the adapters overview.

Svelte version support ​

The adapter detects the major version of Svelte installed in the project and generates the corresponding code:

  • Svelte 5 — components use runes ($props, $derived), the slot via {@render children?.()}; events are regular attributes (onclick, oninput, …).
  • Svelte 4 — export let + reactive declarations $:, the slot via <slot />; all events (on:click, on:input, …) are automatically forwarded to the root element.

The version is read from svelte/package.json relative to the project root and cached. If Svelte cannot be resolved, the adapter prints a warning and uses Svelte 5 as the default.

See also ​