Documentation #
This library contails following components for Svelte Runes project.
-
- Code block style changer
- On This Page
- Code wrapper
- HighlightCompo
- Home cards
- Icon page
- Doc page
- Footer
- Sidebar
- Support banner
- Tech info
- Toc
- Anchor
- Code
- H2, H3
- Helper functions
Use the utils
and +layout.svelte
examples how to set up Nav and DynamicCodeBlockStyle.
Runes Webkit #
If you prefer to use the starter, please refer to https://github.com/shinokada/runes-webkit-starter.
Installation #
pnpm i -D flowbite-svelte svelte-rune-highlight highlight.js runes-webkit svelte-lib-helpers
// install tailwindcss
npx svelte-add@latest tailwindcss
Setting #
-
Enable
:compilerOptions
import adapter from '@sveltejs/adapter-auto'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { preprocess: [vitePreprocess({})], compilerOptions: { runes: true }, kit: { adapter: adapter() } }; export default config;
Layout #
Create +layout.svelte, utils/Nav.svelte, utils/DynamicCodeBlockStyle.svelte
and create
a directory named utils/highlight
and add all styles to it.
-
Add the following to the
+layout.svelte
:<script lang="ts"> import '../app.pcss'; import { sineIn } from 'svelte/easing'; import type { Component } from 'svelte'; import { page } from '$app/stores'; import { newSidebarList } from './utils/helper'; import { Footer, OnThisPage, extract, removeHyphensAndCapitalize, DotsHorizontalOutline, GithubSolid, random_tailwind_color, XSolid, } from 'runes-webkit'; import { Navbar, NavLi, NavBrand, NavUl, uiHelpers, Darkmode, Dropdown, DropdownUl, DropdownLi, Sidebar, SidebarGroup, SidebarDropdownWrapper, SidebarItem, CloseButton, SidebarBrand } from 'flowbite-svelte'; import { RunesMetaTags } from 'runes-meta-tags'; import { Runatics } from 'runatics'; import DynamicCodeBlockStyle from './utils/DynamicCodeBlockStyle.svelte'; type LiType = { name: string; href: string; Icon?: Component; }; let { children, data } = $props(); const analyticsId = data.ANALYTICS_ID; let metaTags = $state($page.data.pageMetaTags ? $page.data.pageMetaTags : data.layoutMetaTags); // sidebar const sidebarUi = uiHelpers(); let isOpen = $state(false); const closeSidebar = sidebarUi.close; let currentUrl = $state($page.url.pathname); const hasPath = (key: string) => currentUrl.includes(key); const lis: LiType[] = [ { name: 'Guide', href: '/guide/svelte-4/getting-started' }, { name: '3-Tabs', href: '/three-tabs' }, { name: '3-Tabs-tailwind', href: '/three-tabs-sizebytailwind' }, { name: 'No-tabs', href: '/no-tabs' }, { name: 'How to use', href: '/how-to-use' }, { name: 'Quick start', href: '/quick-start' } ]; const brand = { name: 'codewithshin.com', href: 'https://codewithshin.com' }; const urlsToIncludeSwitcherAndSidebar = ['/guide/', '/guide2/', '/how-to-use', '/quick-start']; const siteName = removeHyphensAndCapitalize(__NAME__); const twitterUrl = 'https://twitter.com/shinokada'; const githubUrl = `https://github.com/shinokada/${__NAME__}`; // nav let nav = uiHelpers(); let navStatus = $state(false); let toggleNav = nav.toggle; let closeNav = nav.close; let headerCls = 'sticky top-0 z-40 mx-auto w-full flex-none border-b border-gray-200 bg-gray-100 dark:border-gray-600 dark:bg-sky-950'; let navClass = 'w-full divide-gray-200 border-gray-200 bg-gray-50 dark_bg_theme text-gray-500 dark:divide-gray-700 dark:border-gray-700 dark:transparent dark:text-gray-400 sm:px-4'; let divClass = 'ml-auto w-full'; let ulclass = 'dark:lg:bg-transparent lg:space-x-4'; function isIncluded(url: string, allowedUrls: string[]): boolean { return allowedUrls.some((allowedUrl) => url.startsWith(allowedUrl)); } let urlsToIncludeSwitcher = ['/guide'] let include = $derived(isIncluded(currentUrl, urlsToIncludeSwitcher)); // dropdown let dropdown = uiHelpers(); let dropdownStatus = $state(false); let closeDropdown = dropdown.close; let dropdownTransitionParams = { y: 0, duration: 200, easing: sineIn }; // sidebar let iconClass = 'fixed inset-0 z-30 flex-none h-full lg:static lg:h-auto lg:overflow-y-visible bg-white dark_bg_theme lg:pt-0 lg:block' $effect(() => { navStatus = nav.isOpen; dropdownStatus = dropdown.isOpen; currentUrl = $page.url.pathname; metaTags = $page.data.pageMetaTags ? $page.data.pageMetaTags : data.layoutMetaTags; isOpen = sidebarUi.isOpen; }); </script> {#snippet navLi(lis:LiType[])} {#each lis as { name, href, Icon }} {#if Icon} <Icon class="mb-3 h-8 w-8 {random_tailwind_color()}"></Icon> {/if} <NavLi {href}>{name}</NavLi> {/each} {/snippet} <Runatics {analyticsId} /> <RunesMetaTags {...metaTags} /> <header class={headerCls}> <Navbar {navClass} {toggleNav} {closeNav} {navStatus} breakPoint="lg" fluid div2Class={divClass}> {#snippet brand()} {#if urlsToIncludeSwitcherAndSidebar.some((path) => currentUrl.startsWith(path))} <button onclick={sidebarUi.toggle} type="button" class="z-100 mr-4 mt-1 lg:hidden" aria-controls="navbar-default" > <span class="sr-only">Toggle sidebar menu</span> <svg class="h-5 w-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14" > <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15" /> </svg> </button> {/if} {#if siteName} <NavBrand {siteName} spanClass="self-center whitespace-nowrap text-2xl font-semibold text-primary-900 dark:text-primary-500" /> {/if} <div class="ml-auto flex items-center lg:order-1"> {#if include} <div class="hidden sm:block"> <DynamicCodeBlockStyle /> </div> {/if} <DotsHorizontalOutline onclick={dropdown.toggle} class="ml-6 mr-4 dark:text-white" size="lg" /> <div class="relative"> <Dropdown {dropdownStatus} {closeDropdown} params={dropdownTransitionParams} class="absolute -left-[50px] top-2 w-12 p-1.5" > <DropdownUl> {#if twitterUrl} <DropdownLi href={twitterUrl} target="_blank" aClass="p-2 m-0" ><XSolid /></DropdownLi > {/if} {#if githubUrl} <DropdownLi href={githubUrl} target="_blank" aClass="p-2 m-0"> <GithubSolid /> </DropdownLi> {/if} <DropdownLi> <Darkmode class="m-0 p-2" /> </DropdownLi> </DropdownUl> </Dropdown> </div> </div> {/snippet} {#if lis} <NavUl class={ulclass}> {@render navLi(lis)} </NavUl> {/if} </Navbar> </header> <div class="lg:flex"> {#if urlsToIncludeSwitcherAndSidebar.some((path) => currentUrl.startsWith(path))} <Sidebar {isOpen} {closeSidebar} breakpoint="lg" activeClass="flex items-center p-1 text-base font-normal text-white dark:hover:text-white hover:text-gray-900 bg-primary-700 dark:bg-primary-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700" nonActiveClass="p-1 hover:bg-gray-200" divClass="dark:bg-gray-900 bg-gray-50" class="lg:top-[70px] h-screen dark:bg-gray-900"> <CloseButton onclick={closeSidebar} color="gray" class="absolute right-1 top-3 p-2 lg:hidden" /> <SidebarGroup> <SidebarBrand> <span class="self-center whitespace-nowrap text-xl font-semibold dark:text-white">Runes Webkit</span> </SidebarBrand> {#each newSidebarList as { name, Icon, children, href }} {#if children} <SidebarDropdownWrapper label={name} isOpen={hasPath('components')} svgClass="me-4" btnClass="p-1" > {#snippet iconSlot()} <Icon /> {/snippet} {#each children as { name, Icon, href }} <SidebarItem label={name} onclick={closeSidebar} {href} aClass="ml-4"> {#snippet iconSlot()} <Icon /> {/snippet} </SidebarItem> {/each} </SidebarDropdownWrapper> {:else} <SidebarItem label={name} onclick={closeSidebar} {href}> {#snippet iconSlot()} <Icon /> {/snippet} </SidebarItem> {/if} {/each} </SidebarGroup> </Sidebar> {/if} <div class="relative"> <OnThisPage {extract} headingSelector="#mainContent > :where(h2, h3)" /> </div> {@render children()} </div> <Footer {brand} {lis} />
Style #
There are two ways to style components.
Use Developer tools to find CSS class names. It starts and ends with underscore, like _iconPage_div_1_
.
The first part is the abbriviation of the component name and the second part is tag name.
The props names are without underscore, like iconPage_div_1
. Use the props name to
add the styles to a component.
Another way is to add style to the app.pcss
file using a class name with underbars, like _iconPage_div_1_
, etc.
Guide Directory Layout #
Add the following to the guide/+layout.svelte
:
<script lang="ts">
import {DocPage} from "runes-webkit";
import type { Snippet } from "svelte";
interface Props {
children: Snippet;
}
let { children }: Props = $props()
</script>
<DocPage>
{@render children()}
</DocPage>
Home Page #
Home page has SupportBanner, HomeCards, TechInfo
components.
<script lang="ts">
import { removeHyphensAndCapitalize, HomeCards, SupportBanner, TechInfo, BellActiveAltOutline, insertObjectToArray, excludeByTitle, cards, info, pkg } from 'runes-webkit';
import { A } from 'flowbite-svelte';
const cardsToExclude = ['Seven Props']
const brand = {
title: 'Brands, Regular, and Solid Icons',
description: '2000+ SVG Icons.',
Icon: BellActiveAltOutline,
icon_class: 'text-green-500'
}
let filteredCards = $state(insertObjectToArray(excludeByTitle(cards, cardsToExclude), brand, 2))
const runaticsVersion = __RUNATICS_VERSION__;
const runesMetaTagsVersion = __RUNES_METATAGS_VERSION__;
let newPkg = $state({...pkg, runaticsVersion, runesMetaTagsVersion})
</script>
<div class="relative h-full max-w-7xl mx-auto overflow-y-auto px-8">
<SupportBanner>
To Keep It Going, Please Show Your Love.<a href='https://ko-fi.com/Z8Z2CHALG' target='_blank'><img height='40' style='border:0px;height:40px;' src='https://storage.ko-fi.com/cdn/kofi3.png?v=3' alt='Buy Me a Coffee at ko-fi.com' /></a>
</SupportBanner>
<h1 class='flex justify-center my-8'>{removeHyphensAndCapitalize(__NAME__)}</h1>
<h2 class='flex justify-center my-8'><A href ='/how-to-use' aclass='underline'>This is a demo page. Please read How to use page.</A> </h2>
<HomeCards cards={filteredCards}/>
<h2 class='flex justify-center my-8'>Info</h2>
<HomeCards cards={info} />
<TechInfo {...newPkg} />
</div>
Icon Page #
Icon page uses IconPage component with threeTabs and titel props.
-
<script lang="ts"> import type { Component } from 'svelte'; import { IconPage, filterStringKeys } from 'runes-webkit' import * as icons from 'runes-webkit' const keyIcons = filterStringKeys(icons); </script> <IconPage icons={keyIcons as Component} threeTabs={false} title="No Tabs" />
Tests #
Please refer to tests/test.ts
-
import type { MetaProps } from 'runes-meta-tags'; import { ANALYTICS_ID } from '$env/static/private'; import { metaTitle, metaDescription, metaImg } from 'runes-meta-tags'; export const load = ({ url }) => { const siteName = metaTitle('/', __NAME__); const title = metaTitle(url.pathname, __NAME__); const basicDesc = 'A collection of reusable Svelte components for building user interfaces for Runes' const description = metaDescription(url.pathname, basicDesc); const image = metaImg(url.pathname, __NAME__); const keywords = 'svelte, runes, webkit, ui, components'; const layoutMetaTags: MetaProps = { title, description, keywords, twitter: { card: 'summary_large_image', site: '@shinokada', handle: '@shinokada', title, description, image, imageAlt: title, }, og: { type: 'website', title, description, url: url.href, image, imageAlt: title, siteName, imageWidth: '1200', imageHeight: '630' } }; return { layoutMetaTags, ANALYTICS_ID }; };