Style Guide

If you wish to contribute to Skeleton, please review our opinionated code style guide below.


File Name Conventions

  • Feature directories should be singular and title case: ../LightSwitch/..
  • Components should be singular and title case: LightSwitch.svelte
  • Svelte Actions should be singular, lowercase, and use Typescript: clipboard.ts
  • Tailwind Element stylesheets should be plural and lowercase: buttons.css
  • Documentation should be lowercase and use dashes: /routes/components/radio-groups/+page.svelte
  • Tests should be suffixed with *.test.ts and match the feature naming convention: LightSwitch.test.ts

Conventions

Ensure relevant events bubble via event forwarding.

html
<button on:click on:mouseover>Skeleton</button>

Follow conventions set by existing components when naming slots. These should be short, semantic, and agnostic. Avoid names that are too specific, such as name="icon".

html
{#if $$slots.lead}<slot name="lead" />{/if}

Use caution when inlining Tailwind color classes that would clash with custom themes. Ideally use a custom component property to allow for customization.

html
<div class="bg-orange-500">Skeleton</div><div class="bg-secondary-500">Skeleton</div>

If you need to include miscellaneous attributes that were not defined as properties, use Svelte's $$restProps. Be careful though, this can overwrite the element's $$props.class attribute. To avoid this, delete the class key from $$restProps. The function provided below can handle this on both init and after any form of attribute updates.

javascript
function prunedRestProps(): any {
	delete $$restProps.class;
	return $$restProps;
}
html
<button class="... {$$props.class ?? ''}" {...prunedRestProps()}>Skeleton</button>

Property Conventions

Follow these guidelines when creating or adding new component props.

  • Each prop should be a single word, all lowercase, and semantic. Match Tailwind class naming conventions whenever possible.
  • Color props should follow standard CSS style conventions (ex: color for text color).
  • If you need multiple words, use camel-casing (ex: ringWidth).
  • Never pass class props as arrays or objects. Strings work better (ex: border border-primary-500).
  • Always pass the full Tailwind class name. Tailwind does not support contructed class names.
  • Ensure Typescript types are provided and set relevant default values when possible.
  • If a new prop is added or modified then please consider updating the documentation with an example if necessary.

Here's a few examples:

typescript
export let background: string = 'bg-primary-500'; // background color
export let color: string = 'text-primary-500'; // text color
export let rounded: string = 'rounded-xl'; // border radius
export let visible: boolean = false;

CSS Styling Conventions

Skeleton utilizes a specific paradigm for handling static and dynamic Tailwind styles for components. This is accomplished by passing Tailwind classes to the component as props, as well as by defining base structural classes within the component's script tag. While this may feel odd at first, you will find it works really well in execution.

Base Classes

Any core or structural Tailwind classes can be defined as follows. Note the "c" is short for classes.

typescript
let cBase: string = 'bg-surface-500 p-4 rounded'; // parent element styles
let cLabel: string = 'text-base'; // child element label styles

Dynamic Classes

If you expect to set one or more styles based on the current value of a property, handle this within a reactive statement as shown below.

typescript
// Prop for outlined state
export let outlined: boolean;

// Create a reactive property that uses a tertiary statement
$: classesOutlined = outlined ? 'border-2 border-primary-500' : 'border-none';

Reactive Classes

Reactive classes combine all base and dynamic classes. These are applied directly to each respective element.

typescript
$: classesTab = `${cBase} ${classesOutlined}`; // parent element
$: classesLabel = `${cBaseLabel}`; // child element

Classes are applied in three ways:

  1. The first class should be an "identifier" class, providing a selection target for global stylesheet overrides.
  2. The combined set of reactive class values, which can mix base and dynamic classes.
  3. For parent elements, add $$props.classes to supply additional arbitrary classes.
html
<div class="tab {classesTab} {$$props.classes ?? ''}">
	<span class="tab-label {classesLabel}">Label</span>
</div>

Pitfalls

Below are a few pitfalls we've encountered when creating Skeleton. Do your best to avoid these whenever possible.

  • Never construct utility class names, Tailwind does not support this feature.
  • Avoid style blocks and @apply in component files. This can increase the final stylesheet bundle size.
  • Do not mix script-defined and inline Tailwind classes. Doing so can have a negative impact on the readability of the code.
  • Avoid switch-case statements to create shorthand property values (ex: sm, md, lg). This limits customization.
  • Keep Skeleton icon library agnostic. Embed SVGs or unicode instead. Implement a slot with a default so that the user can override if necessary.