Slender UI
Slender-ooyThe JavaScript-less component library for Svelte.
Introduction
To get started, here you go. But first…
Another UI library?
Yes.
Why?
To celebrate the best of HTML and CSS. And I’m tired of implementing the same things in slightly different ways.
Also, JavaScript is abused. Sprinkle it on for a better experience, but start with the basics. And it turns out you can push the basics quite far, so hopefully we can get better experiences for shopping on Tor.
See this by Stuart Langridge.
No JavaScript you say?
Kinda. Most components work without it, but they’re progressively enhanced for accessibility and extra smoothness when JS is available. To manage state, like in a modal or dropdown, there are checkboxes (and occasionally radio buttons). Lots and lots of checkboxes. And some CSS trickery.
Some requirements are outside the realms of HTML hacks, like the Transition
and custom Listbox
or Combobox
components.
Installation
In your SvelteKit (or regular Svelte) project:
npm i slender-ui
If you’re using TailwindCSS
At the top of your project’s app.css
or equivalent:
@import 'slender-ui/app.css' layer(components);
...
And in tailwind.config.cjs
:
module.exports = {
content: [..., require('slender-ui/purge')],
...
}
If you’re not using TailwindCSS
At the top of your +layout.svelte
:
<script>
import 'slender-ui/app.css';
...
Theming
You can theme the components to your heart’s delight with regular CSS or, like us heathens, use TailwindCSS.
CSS
We use CSS variables.
<Button label="Ain't nobody" />
<style>
:root {
--slender-color-neutral: 126, 34, 206; /* Global defaults are defined as RGB triples */
--slender-border-radius: 2px;
}
</style>
Getting specific
Each component uses slender-
+ its lowercase name as a class that you can target directly (e.g. the <Button />
will have a .slender-button
class), but we recommend using custom properties (‘CSS variables’) instead:
<Button label="Ain't nobody" />
<style>
:root {
--slender-button-primary-bg-color: 190, 18, 60;
--slender-button-primary-color: 255, 255, 255;
--slender-button-border-radius: 9001px;
--slender-button-md-padding-horizontal: 5rem;
}
</style>
Variable names follow a --slender-{component}-{?type}-{property}
format, such as --slender-button-background-color
.
You can find the full list of CSS variables here.
TailwindCSS
The above is ideal for a consistent and maintainable design system, but on the odd occasion it’s useful to pass in class names directly:
<Button class="bg-cyan-300 text-cyan-900 hover:bg-cyan-400 rounded-none">
Loves me better
</Button>
Components
Drawer
For all the things your designer forgot about.
<script>
import { Drawer } from 'slender-ui'
</script>
<Drawer.Button
label="Slide on out"
for="slender-drawer"
color="primary"
/>
<Drawer.Body position="left" class="px-4" id="slender-drawer">
<h2>Well hello!</h2>
<p>This is a drawer, what did you expect.</p>
<Drawer.CloseButton variant="soft" label="Close me!" />
</Drawer.Body>
Well hello!
This is a drawer, what did you expect.
Icon
Isn’t it iconic?
All the icons are pre-generated SVGs and use an <img>
tag to serve them, which can be slow in development but is performant in production and dramatically saves bundle size for your users by only loading the necessary icons.
<script>
import { Icon } from 'slender-ui'
</script>
<Icon name="feather.calendar" class="h-14 text-pink-500" />
Modal
Ye olde modal, for the “I’m not sure this needs a new page but I don’t know where to put it on the current page.”
<script>
import { Modal } from 'slender-ui'
</script>
<Modal.Button label="Ain't nobody" color="primary" for="slender-modal-123" />
<Modal.Body id="slender-modal-123">
<h3>Hello.</h3>
<p>Got em.</p>
<Modal.CloseButton label="Close" variant="soft" class="mt-4 w-full shadow-sm" />
</Modal.Body>
Notifications
A toast, a flash, a notification… you get the gist.
Let’s toast, let’s roast, then flash it away. It’s a notification, go hover and it’s here to stay.
Probably my favourite of the components, as these make an SSR’d, no-JS site real slick.
<!-- __layout.svelte -->
<script>
import { Notifications } from 'slender-ui'
</script>
<Notifications>
<slot />
</Notifications>
You can either pass in an array of notifications to the component (great for JS-less SSR)…
<script>
...
const notifications = [
{
title: 'Well hello',
color: 'success'
}
]
</script>
<Notifications {notifications}>
<slot />
</Notifications>
…or use the exported notify
function from anywhere in the component subtree:
<!-- SomeComponent.svelte -->
<script>
import { notify } from 'slender-ui'
notify({ title: "One, two, three o'clock, four o'clock, rock" })
</script>
Although this works without JavaScript, ironically you’ll need it enabled for this demo.
These use some tasty CSS magic to do away with the JS. Having said that, you need to enable it for the demo:
Tabs
<script>
import { Tabs } from 'slender-ui'
</script>
<Tabs.Group>
<div class="flex space-x-2">
<Tabs.Tab label="Anglo" color="primary" />
<Tabs.Tab label="Saxon" color="primary" />
</div>
<div class="px-2 py-1 bg-white rounded shadow mt-2 w-full">
<Tabs.Panel open>
Loves me better.
</Tabs.Panel>
<Tabs.Panel>
Makes me feel this way.
</Tabs.Panel>
</div>
</Tabs.Group>
Transition
Alas, JS only.
<script>
import { Transition, Button } from 'slender-ui'
let key = 0
</script>
<Transition
{key}
to={{ x: -50, rotate: -180, duration: 400 }}
from={{ x: 100, scale: 1.5, rotate: 90, delay: 400 }}
>
<Button iconLeft="feather.refresh-ccw" label="Transition me!" on:mousedown={() => key++} />
</Transition>
Forms
Checkbox
<script>
import { Checkbox } from 'slender-ui'
</script>
<Checkbox class="h-10 w-10" checked color="primary" />
Color: primary
Variant: solid
Combobox (Autocomplete)
For the fancy one you’ll need JavaScript, otherwise it falls back to a <datalist>
.
<script>
import { Combobox } from 'slender-ui'
const options = [
'Birch',
'Apple',
'Beech',
'Alder',
'Hawthorn',
'Hazel',
'Holly',
'Rowan',
'Willow'
]
</script>
<Combobox {options} placeholder="Search for a tree..." color="primary" />
Incrementer
You’re in then you’re out, you’re up then you’re down, you’re wrong when it’s right.
Yup, still no JS.
<script>
import { Incrementer } from 'slender-ui'
</script>
<Incrementer color="primary" min={0} max={10} />
Input
<script>
import { Input } from 'slender-ui'
</script>
<Input
floatingLabel
label="Enter your name"
placeholder="Hello this is a placeholder"
/>
Listbox (Select)
For the fancy one you’ll need JavaScript, otherwise it falls back to a <select>
.
<script>
import { Listbox } from 'slender-ui'
const options = [
'Birch',
'Apple',
'Beech',
'Alder',
'Hawthorn',
'Hazel',
'Holly',
'Rowan',
'Willow'
]
</script>
<Listbox {options} class="w-44" color="primary" />
Color: primary
Variant: solid
Radio
<script>
import { Radio } from 'slender-ui'
</script>
<Radio class="h-10 w-10" name="gaga" color="primary" />
<Radio class="h-10 w-10" name="gaga" color="primary" />
Color: primary
Variant: solid
TextArea
<script>
import { TextArea } from 'slender-ui'
</script>
<TextArea
floatingLabel
label="Enter your thoughts"
/>
Toggle
A switch, a toggle, the mind does it boggle; this is no fox but a fancy checkbox!
<script>
import { Toggle } from 'slender-ui'
</script>
<Toggle size="large" color="primary" />
Color: primary
Variant: solid