"After playing around with Replicant, I realized I could build Web Components without React — actually, even without Replicant.
I'm wondering if it's called 'Replicant' because of Blade Runner (I love Blade Runner).
Maybe I could name my own library something similar... Tyrell? No, that feels pretentious.
Anyway, I don't really want to type something long like
tyrell-button. It should be shorter — maybety-button.Yes! Let's call it ty."
Framework-agnostic web components with a unique dual architecture:
- TypeScript Components - Modern, type-safe UI components
- ClojureScript Infrastructure - Advanced routing, i18n, and documentation site
- Zero Dependencies - Built on web standards that won't break
Works with React, Vue, HTMX—or no framework at all.
ty is actively being developed. Components work in production, examples run smoothly, but expect rough edges. This is a real project with a real vision - web components that work everywhere, built on standards that won't break next year.
- ✅ Button - Semantic buttons with flavors and sizes
- ✅ Input - Enhanced inputs with validation and formatting
- ✅ Textarea - Auto-resizing textarea
- ✅ Checkbox - Styled checkbox with form integration
- ✅ Calendar - Full calendar system with navigation and custom rendering
- ✅ Calendar Month - Standalone month view
- ✅ Calendar Navigation - Calendar controls
- ✅ Date Picker - Date selection with calendar popup
- ✅ Dropdown - Rich dropdown with HTML content support
- ✅ Multiselect - Multi-select with tags and search
- ✅ Modal - Accessible modals with focus trapping
- ✅ Popup - Positioned popovers
- ✅ Tooltip - Smart tooltips with positioning
- ✅ Tag - Chip/tag component with removable option
- ✅ Icon - Icon component with registry system
- ✅ Copy - Copy-to-clipboard for API keys, tokens, URLs
- ✅ Tabs - Tab navigation with content panels
- ✅ Tab - Individual tab component
- 5-Variant Color System - From
ty-text--(faint) toty-text++(strong) - Automatic Dark Mode - Intelligent emphasis flipping
- 130+ CSS Variables - Complete customization
- 7 Semantic Colors - primary, secondary, success, danger, warning, info, neutral
- 5 Surface Levels - canvas, content, elevated, floating, input
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My App</title>
<!-- Ty CSS and JS from CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gersak/ty/dist/ty.css">
<script src="https://cdn.jsdelivr.net/npm/@gersak/ty/dist/ty.js"></script>
</head>
<body>
<ty-button flavor="primary">Hello World</ty-button>
<ty-calendar value="2024-12-25"></ty-calendar>
</body>
</html>npm install @gersak/tyimport '@gersak/ty/css/ty.css'
import { TyButton, TyCalendar } from '@gersak/ty'Icons are optional and loaded separately:
// Import specific icons (tree-shakeable)
import { check, heart, save } from '@gersak/ty/icons/lucide'
// Register with global API
window.tyIcons.register({ check, heart, save })Then use:
<ty-icon name="check"></ty-icon>Available Icon Libraries:
- Lucide: 1,636 icons (tree-shakeable)
- Heroicons: 4 variants
- Material Design: 5 variants
- FontAwesome: 3 variants
For React projects, use the React wrapper package:
npm install @gersak/ty-reactimport React, { useState } from 'react'
import { TyButton, TyInput, TyIcon } from '@gersak/ty-react'
import { check, heart, save } from '@gersak/ty/icons/lucide'
// Register icons
window.tyIcons.register({ check, heart, save })
function App() {
const [name, setName] = useState('')
return (
<div className="ty-elevated p-6 rounded-lg">
<h2 className="ty-text++ text-xl mb-4">Hello Ty!</h2>
<TyInput
value={name}
placeholder="Enter name"
onChange={(e) => setName(e.target.value)}
/>
<TyButton
flavor="primary"
onClick={() => alert('Hello ' + name)}
>
<TyIcon name="check" />
Submit
</TyButton>
</div>
)
}Setup HTML (include Ty CSS and JS):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My React App</title>
<!-- Ty CSS and JS from CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gersak/ty/dist/ty.css">
<script src="https://cdn.jsdelivr.net/npm/@gersak/ty/dist/ty.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html><ty-input
hx-post="/api/search"
hx-trigger="input changed delay:300ms"
hx-target="#results"
placeholder="Search..."
/>Just works. Server-side rendering with dynamic interactions.
Import the components, use them. Web Components are web standards.
For ClojureScript projects, use the React wrapper with tree-shakeable icon imports:
Add dependencies to deps.edn:
{:deps {com.pitch/uix.core {:mvn/version "1.1.0"}
com.pitch/uix.dom {:mvn/version "1.1.0"}
dev.gersak/ty-icons {:mvn/version "LATEST"} ; Tree-shakeable icons
dev.gersak/ty {:mvn/version "LATEST"}}} ; Optional: Router, i18n, layoutImport icons from ClojureScript (not JavaScript):
(ns my-app.core
(:require ["@gersak/ty-react" :as ty]
[ty.lucide :as lucide])) ; ← ClojureScript import, not JavaScript!
;; Register only the icons you need (Google Closure Compiler tree-shakes unused icons)
(js/window.tyIcons.register
#js {"check" lucide/check
"heart" lucide/heart
"save" lucide/save})
(defn app []
[:div.ty-elevated.p-6.rounded-lg
[:h2.ty-text++.text-xl.mb-4 "Hello Ty!"]
[:> ty/Button {:flavor "primary"
:on-click #(js/alert "Clicked!")}
[:> ty/Icon {:name "check"}]
"Submit"]])- ✅ Correct:
[ty.lucide :as lucide]- Google Closure Compiler eliminates unused icons - ❌ Wrong:
["@gersak/ty/icons/lucide" :refer [check]]- Bundles ALL 1,636 icons (~897KB)
By importing from the ClojureScript artifact (dev.gersak/ty-icons), only the icons you reference are included in your production bundle.
Bonus: Built-in router, i18n, and responsive layout system via dev.gersak/ty Clojars package.
Ty components require the ty.css stylesheet to display correctly. The CSS file contains:
- CSS Variables - Design tokens for all colors, surfaces, spacing, and typography
- Utility Classes - Semantic classes like
.ty-bg-primary,.ty-text++,.ty-elevated - Theme System - Light/dark mode definitions that swap automatically
- Component Styles - Base styling that components depend on
TypeScript Components (packages/core/)
- All 18 UI components written in modern TypeScript
- Published to NPM as
@gersak/ty - Zero runtime dependencies
- Full
.d.tstype definitions - Vite build with Terser minification
ClojureScript Infrastructure (packages/cljs/)
- Tree-based routing with authorization
- Protocol-based i18n with Intl API
- Context management for responsive design
- Documentation site and examples
- Published to Clojars as
dev.gersak/ty
Why This Matters:
- TypeScript devs: Get modern components with type safety, no ClojureScript needed
- ClojureScript devs: Get powerful infrastructure + TypeScript components
- Everyone: Components work everywhere, regardless of tech stack
This project grows with community input. Every issue, PR, and discussion helps shape the direction.
Ways to contribute:
- 🐛 Report Issues - Found a bug? Let us know
- 🌟 Star on GitHub - Show support for the project
- 🔧 Pull Requests - Documentation, components, examples - all contributions matter
Especially interested in:
- Mobile interaction improvements
- New components
- Real-world usage feedback
- Documentation improvements
- Icon library expansions
- 📖 Docs & Examples: gersak.github.io/ty
- 💻 GitHub: github.com/gersak/ty
- 📦 NPM (TypeScript): @gersak/ty
- ⚛️ NPM (React): @gersak/ty-react
- 📦 Clojars (ClojureScript): dev.gersak/ty
- 🌐 CDN: jsdelivr.net/npm/@gersak/ty
- 🚧 Better mobile adaptations
- 🚧 Enhanced accessibility features
Built with TypeScript for universal compatibility. Powered by ClojureScript for advanced features. Framework-agnostic by design.
MIT Licensed. Work in progress. Getting better every day.