Skip to content

Latest commit

History

367 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

@uniweb/kit

Standard component library for Uniweb foundations. Tree-shakeable utilities, components, and hooks for building foundation components.

Installation

npm install @uniweb/kit

Tree-Shaking Benefits

Kit is designed to be bundled into your foundation (not externalized like @uniweb/core). This means:

  • Only what you use is bundled — Import 3 components? Only those 3 end up in your foundation
  • No runtime overhead — Unused code is eliminated at build time
  • Customizable — Override or extend any component without carrying dead code
  • Small foundations — A minimal foundation using just Link and useWebsite stays tiny
// vite.config.js - Kit is bundled, core is externalexportdefault{build: {rollupOptions: {external: ['react','react-dom','react-router-dom','@uniweb/core']// Note: @uniweb/kit is NOT in external — it gets tree-shaken}}}

Quick Start

import{Link,Image,useWebsite}from'@uniweb/kit'functionHero({ content }){const{ localize }=useWebsite()return(<div><Imagesrc={content.images[0]?.url}alt="Hero"/><h1>{localize({en: 'Welcome',es: 'Bienvenido'})}</h1><Linkto="/about">Learn More</Link></div>)}

Components

Link

Smart link component with routing, downloads, and auto-generated accessible titles.

import{Link}from'@uniweb/kit'<Linkto="/about">About</Link><Linkhref="https://github.com">GitHub</Link><Linkhref="/files/report.pdf">Download Report</Link><Linkhref="mailto:hello@example.com">Contact</Link>
PropTypeDescription
to / hrefstringDestination URL
titlestringTooltip (auto-generated if omitted)
targetstringLink target
downloadbooleanForce download behavior

Image

Versatile image component with filters and profile integration.

import{Image}from'@uniweb/kit'<Imagesrc="/photo.jpg"alt="Photo"/><Imagesrc="/photo.jpg"filter={{grayscale: 100}}/><Imageprofile={profile}type="avatar"size="lg"rounded/>
PropTypeDescription
src / urlstringImage URL
altstringAlt text
sizestringPreset: xs, sm, md, lg, xl, 2xl, full
roundedboolean|stringBorder radius
filterobjectCSS filters: blur, brightness, contrast, grayscale, saturate, sepia
profileobjectProfile for avatar/banner images
typestringImage type: avatar, banner

SafeHtml

Safely render HTML with topic link resolution.

import{SafeHtml}from'@uniweb/kit'<SafeHtmlvalue="<p>Hello <strong>World</strong></p>"/><SafeHtmlvalue='<a href="topic:about">About</a>'/>

Icon

SVG icon component with built-in icons and URL loading.

import{Icon}from'@uniweb/kit'<Iconname="check"size="24"color="green"/><Iconurl="/icons/custom.svg"/><Iconsvg="<svg>...</svg>"/>

Built-in: check, alert, user, heart, settings, star, close, menu, chevronDown, chevronRight, externalLink, download, play

SocialIcon

Social media platform icons with automatic detection.

import{SocialIcon,getSocialPlatform,filterSocialLinks}from'@uniweb/kit'<SocialIconplatform="twitter"size={24}/><SocialIconurl="https://twitter.com/example"/>// UtilitiesgetSocialPlatform('https://linkedin.com/in/user')// 'linkedin'filterSocialLinks(links)// Filter to only social links

Supported: facebook, twitter, x, linkedin, instagram, youtube, github, medium, pinterest, tiktok, discord, mastodon, bluesky, email, phone, orcid, researchgate, googlescholar

Typography

Smart typography components for rendering semantic parser output.

import{Text,H1,H2,P,PlainText}from'@uniweb/kit'<H1text="Main Title"/><H2text={["Multi-line","Subtitle"]}/><Ptext="A paragraph of content"/><Ptext={["First paragraph","Second paragraph"]}/>// Plain text (HTML shown as text)<PlainTexttext="Show <strong>tags</strong> as text"/>
PropTypeDescription
textstring|string[]Content to render
asstringTag: 'h1'-'h6', 'p', 'div', 'span'
htmlbooleanRender as HTML (default: true)
lineAsstringTag for array items

Aliases: H1, H2, H3, H4, H5, H6, P, Span, Div, PlainText

Media

Video player for YouTube, Vimeo, and local videos.

import{Media}from'@uniweb/kit'<Mediasrc="https://youtube.com/watch?v=abc123"/><Mediasrc="/videos/intro.mp4"controls/><Mediasrc="https://youtube.com/..."thumbnail="/poster.jpg"facade/>

FileLogo

File type icons based on filename.

import{FileLogo}from'@uniweb/kit'<FileLogofilename="report.pdf"size="32"/>

MediaIcon

Platform icons (YouTube, Vimeo, etc.).

import{MediaIcon}from'@uniweb/kit'<MediaIcontype="youtube"size="24"/>

Asset

File preview with download functionality.

import{Asset}from'@uniweb/kit'<Assetvalue="document.pdf"profile={profile}/>

Hooks

useWebsite

Access website instance and utilities.

import{useWebsite}from'@uniweb/kit'functionMyComponent(){const{
website,// Website instance
localize,// Localize multilingual values
makeHref,// Transform hrefs (topic:, locale prefixes)
getLanguage,// Current language code
getLanguages // Available languages}=useWebsite()return<div>{localize({en: 'Hello',fr: 'Bonjour'})}</div>}

useActiveRoute

Detect active navigation state.

import{useActiveRoute}from'@uniweb/kit'functionNavLink({ page }){const{ isActive, isActiveOrAncestor }=useActiveRoute()return(<Linkto={page.route}className={isActiveOrAncestor(page) ? 'font-bold' : ''}>{page.title}</Link>)}

useScrolled

Detect scroll position for sticky headers.

import{useScrolled}from'@uniweb/kit'functionHeader(){constscrolled=useScrolled(50)// Threshold in pixelsreturn(<headerclassName={scrolled ? 'shadow-md' : ''}>
...
</header>)}

useMobileMenu

Mobile menu state management.

import{useMobileMenu}from'@uniweb/kit'functionNavbar(){const{ isOpen, toggle, close }=useMobileMenu()return(<><buttononClick={toggle}>Menu</button>{isOpen&&<MobileMenuonClose={close}/>}</>)}

useAccordion

Accordion/FAQ state management.

import{useAccordion}from'@uniweb/kit'functionFAQ({ items }){const{ isOpen, toggle }=useAccordion()returnitems.map((item,i)=>(<divkey={i}><buttononClick={()=>toggle(i)}>{item.question}</button>{isOpen(i)&&<p>{item.answer}</p>}</div>))}

useInView

Viewport intersection detection for lazy loading and animations.

import{useInView,useIsInView}from'@uniweb/kit'functionAnimatedSection(){const{ ref, inView }=useInView({threshold: 0.2,once: true})return(<divref={ref}className={inView ? 'animate-fade-in' : 'opacity-0'}>
Content appears when scrolled into view
</div>)}// Simple boolean versionfunctionLazyImage({ src }){const[ref,isInView]=useIsInView()return<divref={ref}>{isInView&&<imgsrc={src}/>}</div>}

useGridLayout

Responsive grid utilities.

import{useGridLayout,getGridClasses}from'@uniweb/kit'functionGallery({ items }){const{ columns }=useGridLayout(items.length,{maxColumns: 4})return(<divclassName={getGridClasses(columns)}>{items.map(item=><Cardkey={item.id}{...item}/>)}</div>)}

Theme Hooks

Access site theming data at runtime.

import{useThemeData,useThemeColor,useThemeColorVar,useColorContext,useAppearance}from'@uniweb/kit'functionThemedComponent({ block }){// Full theme accessconsttheme=useThemeData()constpalettes=theme?.getPaletteNames()// ['primary', 'secondary', ...]// Get specific colorconstprimaryColor=useThemeColor('primary',500)// '#3b82f6'constprimaryVar=useThemeColorVar('primary',600)// 'var(--primary-600)'// Context-aware (light/medium/dark sections)constcontext=useColorContext(block)// 'light' | 'medium' | 'dark'// Dark modeconst{ scheme, toggle, canToggle }=useAppearance()return(<divstyle={{color: primaryColor}}>{canToggle&&(<buttononClick={toggle}>{scheme==='dark' ? 'Light' : 'Dark'}</button>)}</div>)}

Search

Full-text search powered by Fuse.js. Requires fuse.js as a peer dependency in your foundation.

npm install fuse.js

useSearch

Main search hook with debouncing and state management.

import{useSearch,useWebsite}from'@uniweb/kit'functionSearchComponent(){const{ website }=useWebsite()const{ query, results, isLoading, isEnabled, preload }=useSearch(website)if(!isEnabled)returnnullreturn(<div><inputonChange={e=>query(e.target.value)}placeholder="Search..."/>{isLoading&&<span>Searching...</span>}{results.map(r=>(<akey={r.id}href={r.href}>{r.title}</a>))}</div>)}

useSearchWithIntent

Intent-based preloading — loads search index on hover/focus instead of page load.

import{useSearchWithIntent,useSearchShortcut}from'@uniweb/kit'functionSearchButton({ onClick }){const{ website }=useWebsite()const{ triggerPreload, intentProps }=useSearchWithIntent(website)// Cmd/Ctrl+K shortcut with preloaduseSearchShortcut({onOpen: onClick,onPreload: triggerPreload,})return(<buttononClick={onClick}{...intentProps}>
Search
</button>)}

This saves bandwidth — the search index only loads when users show intent to search.

useSearchShortcut

Keyboard shortcut for opening search.

import{useSearchShortcut}from'@uniweb/kit'// SimpleuseSearchShortcut(()=>setSearchOpen(true))// With preload on shortcutuseSearchShortcut({onOpen: ()=>setSearchOpen(true),onPreload: ()=>searchClient.preload()})

createSearchClient

Low-level search client for advanced use.

import{createSearchClient}from'@uniweb/kit'constclient=createSearchClient(website,{fuseOptions: {threshold: 0.3},defaultLimit: 10})// Query — returns SearchResult[]constresults=awaitclient.query('authentication',{limit: 5})// Same query, plus how many matched before `limit` — the 47 in "showing 10 of 47".// `total` is null when the active provider cannot say (a deployment fact, not an// error): the local index always knows it; an endpoint knows it only if it// reports one. Render the count conditionally, the results unconditionally.const{results: page, total }=awaitclient.queryWithTotal('authentication',{limit: 5})// Preload indexawaitclient.preload()// Check statusclient.isEnabled()client.getIndexUrl()

Styled Components

Pre-styled components with Tailwind CSS.

import{Section,SidebarLayout,Disclaimer}from'@uniweb/kit'<Sectionwidth="lg"padding="md"className="bg-gray-50"><h1>Welcome</h1></Section><SidebarLayoutsidebar={<Nav/>}sidebarPosition="left"><main>Content</main></SidebarLayout><Disclaimertitle="Terms of Service"content="<p>Please read our terms...</p>"triggerText="View Terms"/>

Utilities

import{cn,stripTags,isExternalUrl,isFileUrl,detectMediaType}from'@uniweb/kit'// Merge Tailwind classes (uses tailwind-merge)cn('px-4 py-2','bg-blue-500',condition&&'opacity-50')// Strip HTML tagsstripTags('<p>Hello</p>')// "Hello"// URL utilitiesisExternalUrl('https://google.com')// trueisFileUrl('/files/doc.pdf')// truedetectMediaType('https://youtube.com/...')// 'youtube'

Architecture

┌─────────────────────────────────────────────────────────────┐
│ Foundation (your code) │
│ ├── imports @uniweb/kit (bundled, tree-shaken) │
│ └── @uniweb/core marked as external │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ @uniweb/runtime (browser) │
│ ├── Loads foundation dynamically │
│ ├── Provides @uniweb/core singleton │
│ └── Orchestrates React rendering │
└─────────────────────────────────────────────────────────────┘

Why bundle kit but externalize core?

  • Kit: Different foundations may use different subsets of kit. Tree-shaking ensures each foundation only includes what it uses.

  • Core: Contains the Website, Page, and Block classes that must be singletons. The runtime provides these — foundations reference them via the external import.

License

Apache-2.0

About

Toolkit to create and manage Uniweb projects.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages