New:Socket for Asana Is Now Available.Learn more
Get Started

@ios26_design_system/svelte-inertia

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ios26_design_system/svelte-inertia

iOS 26 design system for Svelte 5 + Inertia.js + Rails — components, layouts, tokens

latest
Source
npmnpm
Version
1.0.0
Version published
Maintainers
1
Created
Source

iOS 26 Design System -- Svelte 5 + Inertia.js + Rails

Complete iOS 26 design token and component library for Group A stack projects (Rails 8 + Inertia.js + Svelte 5 + Vite).

Source: Figma Community Kit

Files

FileContents
tokens.css50 color tokens, typography scale, material tokens, spacing, animations. Light + dark mode.
typography.css11-level SF Pro type scale with Regular/Emphasized/Italic/Loose variants.
materials.cssLiquid Glass (4 sizes), background blur (5 levels), scroll edges, overlays.
components.svelte.md7 iOS components mapped to Svelte 5 + Inertia.js patterns with code.
layout.svelte.mdLayout patterns: root layout, transitions, shared toolbar, error handling.

Setup

1. Copy CSS files to your Rails project

app/
  frontend/                    # or resources/js/ depending on Inertia setup
    css/
      ios26/
        tokens.css
        typography.css
        materials.css
    Components/
      ios/
        Toolbar.svelte
        TabBar.svelte
        ListRow.svelte
        Alert.svelte
        Sheet.svelte
        SegmentedControl.svelte
    Layouts/
      AppLayout.svelte
    Pages/
      ...

2. Import in your app entry CSS

/* app/frontend/css/app.css */

/* iOS 26 Design System */
@import './ios26/tokens.css';
@import './ios26/typography.css';
@import './ios26/materials.css';

/* Your app styles */
/* ... */

3. Vite configuration

// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { resolve } from 'path';

export default defineConfig({
  plugins: [svelte()],
  resolve: {
    alias: {
      $lib: resolve('./app/frontend/Components'),
    },
  },
  css: {
    devSourcemap: true,
  },
});

4. Inertia app initialization

// app/frontend/entrypoints/inertia.js
import { createInertiaApp } from '@inertiajs/svelte';
import '../css/app.css';

createInertiaApp({
  resolve: (name) => {
    const pages = import.meta.glob('../Pages/**/*.svelte', { eager: true });
    return pages[`../Pages/${name}.svelte`];
  },
  setup({ el, App }) {
    new App({ target: el });
  },
});

Dark Mode

Three mechanisms are supported, in priority order:

  • data-theme attribute (explicit) -- Set by user toggle or JavaScript.

    <html data-theme="dark">
    
  • prefers-color-scheme media query (automatic) -- Respects OS setting when data-theme is absent.

  • Combination -- The CSS includes both. If data-theme="light" is set explicitly, it overrides the OS dark mode preference.

The AppLayout.svelte in layout.svelte.md includes an onMount hook that auto-detects OS preference and sets data-theme accordingly.

Full Page Example

Rails Controller

# app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
  def index
    contacts = current_user.contacts.order(:name)

    render inertia: 'Contacts/Index', props: {
      title: 'Contacts',
      contacts: contacts.map { |c|
        {
          id: c.id,
          name: c.name,
          subtitle: c.phone_number,
        }
      }
    }
  end

  def show
    contact = current_user.contacts.find(params[:id])

    render inertia: 'Contacts/Show', props: {
      title: contact.name,
      contact: contact.as_json(only: [:id, :name, :phone_number, :notes]),
    }
  end
end

Svelte Page Component

<!-- app/frontend/Pages/Contacts/Index.svelte -->
<script>
  import Toolbar from '$lib/ios/Toolbar.svelte';
  import ListRow from '$lib/ios/ListRow.svelte';
  import Sheet from '$lib/ios/Sheet.svelte';
  import { useForm } from '@inertiajs/svelte';

  let { contacts } = $props();

  let showAddSheet = $state(false);
  let form = useForm({ name: '', phone_number: '' });

  function handleAdd(e) {
    e.preventDefault();
    $form.post('/contacts', {
      onSuccess: () => {
        showAddSheet = false;
        $form.reset();
      },
    });
  }
</script>

<Toolbar largeTitle>
  {#snippet trailing()}
    <button
      class="ios-body"
      style:color="var(--ios-color-blue)"
      onclick={() => showAddSheet = true}
      type="button"
    >
      Add
    </button>
  {/snippet}
</Toolbar>

<div class="ios-grouped-page">
  <section class="ios-section">
    <div class="ios-section__body">
      {#each contacts as contact (contact.id)}
        <ListRow
          href="/contacts/{contact.id}"
          title={contact.name}
          subtitle={contact.subtitle}
        />
      {:else}
        <div class="ios-empty-state">
          <p class="ios-title3 ios-text-secondary">No Contacts</p>
          <p class="ios-body ios-text-tertiary">
            Add contacts to start sending messages.
          </p>
        </div>
      {/each}
    </div>
  </section>
</div>

<Sheet
  bind:open={showAddSheet}
  title="New Contact"
  leadingAction={{ label: 'Cancel', onclick: () => showAddSheet = false }}
  trailingAction={{ label: 'Save', onclick: handleAdd }}
>
  <form onsubmit={handleAdd} style:padding="var(--ios-padding-horizontal)">
    <div class="ios-form-section">
      <div class="ios-form-section__body">
        <div class="ios-list-row" style:padding="11px var(--ios-padding-horizontal)">
          <input
            type="text"
            class="ios-text-input ios-body"
            placeholder="Name"
            bind:value={$form.name}
            style:width="100%"
          />
        </div>
        <div class="ios-list-row" style:padding="11px var(--ios-padding-horizontal)">
          <input
            type="tel"
            class="ios-text-input ios-body"
            placeholder="Phone Number"
            bind:value={$form.phone_number}
            style:width="100%"
          />
        </div>
      </div>
    </div>
  </form>
</Sheet>

<style>
  .ios-grouped-page {
    padding-top: 16px;
  }

  .ios-section {
    margin-bottom: var(--ios-section-spacing);
  }

  .ios-section__body {
    background: var(--ios-bg-grouped-secondary);
    border-radius: 10px;
    margin: 0 var(--ios-padding-horizontal);
    overflow: hidden;
  }

  .ios-empty-state {
    text-align: center;
    padding: 60px var(--ios-padding-horizontal);
  }

  .ios-empty-state p {
    margin: 0;
  }

  .ios-empty-state p + p {
    margin-top: 8px;
  }

  .ios-form-section__body {
    background: var(--ios-bg-grouped-secondary);
    border-radius: 10px;
    overflow: hidden;
  }

  .ios-text-input {
    background: none;
    border: none;
    color: var(--ios-label-primary);
    outline: none;
  }

  .ios-text-input::placeholder {
    color: var(--ios-label-tertiary);
  }
</style>

Token Count Summary

CategoryCount
System Colors12
Labels4
Backgrounds6
Fills4
Grays8
Separators2
Vibrant Labels (light + dark)8
Vibrant Fills (light + dark)6
Total Color Tokens50
Typography (11 levels x props)50+
Material (Liquid Glass + Blur)~22
Spacing & Layout14
Animation6
Grand Total~142
  • Design guide source: docs/ios26-design-guide.md
  • Figma kit: Community Kit
  • Flutter tokens: docs/ios26-design/flutter/ios26_colors.dart
  • Svelte-only tokens: docs/ios26-design/svelte/tokens.css

Keywords

ios26

FAQs

Package last updated on 30 Mar 2026

Related posts