This is a step-by-step study in converting Heydon Pickering’s progressively-enhanced inclusive tabbed interface example (from his excellent Inclusive Components) to Svelte.
The project is built using SvelteKit.
Each state of the conversion is captured in a tag:
Step 1: Copy and paste. The code is formatted as a Svelte component. :global()
attributes are added to the styles.
Why are we doing everything in onMount()
? For two reasons: one because we are using SSR and the code currently uses browser-only features. And two, because we want to maintain the progressive enhancement aspect. We don’t want, for example, to set ARIA roles that would be misleading or hide content on initial load on the server-side render if, say, the person has JavaScript disabled in their browser.
Step 2: DOM away with you. The DOM structure is split up into separate Svelte components, named according to ARIA role (TabList, Tab, TabPanel, etc.). It is still progressively enhanced and the behaviour is still controlled by the DOM-based global script.
Step 3: Context is everything. The code is split up and encapsulated within the various constituent components. To communicate, the components use the component context and reactive stores.
The index now only contains the component declaration and content.
The model is defined in the top-level TabbedInterface component and added to the component’s context as a store.
Normally, I would keep controller logic in parent components and have child components communicate with their parents using events. However, you can’t currently do that if you use component composition with slots so most of the logic is implemented in the Tab component.
Basically, we’re using reactive dependency injection of sorts to create a two-way communication system. You could do this via other means (e.g., having child components register themselves with their parents, etc., but those are just workarounds for the same limitations in Svelte and would likely end up far more verbose to implement). Given we can’t use events, this is probably the simplest thing that could possibly work in this instance.
(That said, I’m still very new to Svelte so if there’s a better way of doing this, I’m all ears. Start a discussion.)
The component is now fully functional in the demo but not ready to be published as a standalone component yet. I’m waiting for this issue to be resolved with this pull request before I do that so we can have properly-scoped styles.
Install the dependencies
npm install
Run SvelteKit
npm run dev
Hit http://localhost:3000 in your browser.
License: ISC