This commit is contained in:
2026-01-26 09:05:48 +08:00
commit f218a21377
20 changed files with 7774 additions and 0 deletions

26
ui/appwindow.slint Normal file
View File

@@ -0,0 +1,26 @@
import { VerticalBox, Button } from "std-widgets.slint";
export component AppWindow inherits Window {
in-out property <string> greeting: "Hello, World!";
width: 400px;
height: 300px;
title: "Slint Hello World";
VerticalBox {
alignment: center;
Text {
text: greeting;
font-size: 24px;
horizontal-alignment: center;
}
Button {
text: "Click Me!";
clicked => {
greeting = "Hello from Slint!";
}
}
}
}

47
ui/components/badge.slint Normal file
View File

@@ -0,0 +1,47 @@
// Badge Component
// Small status indicator or label
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
export component Badge {
// Public properties
in property <string> text: "Badge";
in property <string> variant: "default"; // default | secondary | destructive | outline
// Calculate colors based on variant
private property <color> bg-color: {
if variant == "destructive" { Theme.colors.destructive }
else if variant == "secondary" { Theme.colors.secondary }
else if variant == "outline" { Colors.transparent }
else { Theme.colors.primary }
};
private property <color> text-color: {
if variant == "destructive" { Theme.colors.destructive-foreground }
else if variant == "secondary" { Theme.colors.secondary-foreground }
else if variant == "outline" { Theme.colors.foreground }
else { Theme.colors.primary-foreground }
};
// Fixed height for badge
height: 24px;
Rectangle {
background: bg-color;
border-radius: 12px; // Fixed radius for consistent rounded look
border-width: variant == "outline" ? 1px : 0px;
border-color: Theme.colors.border;
// Center the text
Text {
text: root.text;
font-size: Typography.sizes.xs;
font-weight: Typography.weights.medium;
color: text-color;
vertical-alignment: center;
horizontal-alignment: center;
x: 10px;
width: parent.width - 20px;
}
}
}

102
ui/components/button.slint Normal file
View File

@@ -0,0 +1,102 @@
// Button Component
// Shadcn-style button with multiple variants and sizes
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
export component Button {
// Public properties
in property <string> text: "Button";
in property <string> variant: "default"; // default | destructive | outline | secondary | ghost
in property <string> size: "md"; // sm | md | lg
in property <bool> disabled: false;
// Callbacks
callback clicked();
// Internal state
private property <bool> hovered: false;
// Calculate background color based on variant
private property <color> base-bg-color: {
if disabled { Theme.colors.muted }
else if variant == "destructive" { Theme.colors.destructive }
else if variant == "outline" { Colors.transparent }
else if variant == "secondary" { Theme.colors.secondary }
else if variant == "ghost" { Colors.transparent }
else { Theme.colors.primary }
};
// Calculate hover background color
private property <color> hover-bg-color: {
if disabled { Theme.colors.muted }
else if variant == "destructive" { #dc2626 }
else if variant == "outline" { Theme.colors.accent }
else if variant == "secondary" { #e4e4e7 }
else if variant == "ghost" { Theme.colors.accent }
else { #27272a }
};
// Calculate text color based on variant
private property <color> text-color: {
if disabled { Theme.colors.muted-foreground }
else if variant == "destructive" { Theme.colors.destructive-foreground }
else if variant == "outline" { Theme.colors.foreground }
else if variant == "secondary" { Theme.colors.secondary-foreground }
else if variant == "ghost" { Theme.colors.foreground }
else { Theme.colors.primary-foreground }
};
// Calculate size-based dimensions
private property <length> btn-height: size == "sm" ? 36px : size == "lg" ? 44px : 40px;
private property <length> btn-padding-x: size == "sm" ? 12px : size == "lg" ? 24px : 16px;
private property <length> font-size: size == "sm" ? Typography.sizes.sm : size == "lg" ? Typography.sizes.lg : Typography.sizes.base;
min-width: btn-height;
min-height: btn-height;
// Main container
container := Rectangle {
background: hovered ? hover-bg-color : base-bg-color;
border-radius: SpacingSystem.radius.md;
border-width: variant == "outline" ? 1px : 0px;
border-color: Theme.colors.border;
// Smooth transitions
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
// Content layout
HorizontalLayout {
padding-left: btn-padding-x;
padding-right: btn-padding-x;
spacing: SpacingSystem.spacing.s2;
alignment: center;
// Button text
Text {
text: root.text;
color: text-color;
font-size: font-size;
font-weight: Typography.weights.medium;
vertical-alignment: center;
horizontal-alignment: center;
}
}
// Touch interaction area
TouchArea {
enabled: !disabled;
clicked => {
root.clicked();
}
moved => {
hovered = self.has-hover;
}
}
}
}

93
ui/components/card.slint Normal file
View File

@@ -0,0 +1,93 @@
// Card Component
// Container for grouping related content with optional hover effect
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
// Main Card component
export component Card {
in property <bool> hoverable: false;
private property <bool> hovered: false;
// Main container
Rectangle {
background: Theme.colors.card;
border-radius: SpacingSystem.radius.lg;
border-width: 1px;
border-color: Theme.colors.border;
// Hover shadow effect
drop-shadow-blur: hovered && hoverable ? 8px : 0px;
drop-shadow-color: #00000020;
drop-shadow-offset-y: hovered && hoverable ? 4px : 0px;
animate drop-shadow-blur, drop-shadow-offset-y {
duration: Animations.durations.normal;
easing: Animations.ease-in-out;
}
VerticalLayout {
padding: SpacingSystem.spacing.s6;
spacing: SpacingSystem.spacing.s4;
@children
}
if hoverable: TouchArea {
moved => {
hovered = self.has-hover;
}
}
}
}
// Card Header component
export component CardHeader {
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
@children
}
}
// Card Title component
export component CardTitle {
in property <string> text;
Text {
text: root.text;
font-size: Typography.sizes.xl;
font-weight: Typography.weights.semibold;
color: Theme.colors.card-foreground;
wrap: word-wrap;
}
}
// Card Description component
export component CardDescription {
in property <string> text;
Text {
text: root.text;
font-size: Typography.sizes.sm;
color: Theme.colors.muted-foreground;
wrap: word-wrap;
}
}
// Card Content component
export component CardContent {
VerticalLayout {
spacing: SpacingSystem.spacing.s4;
@children
}
}
// Card Footer component
export component CardFooter {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: end;
@children
}
}

116
ui/components/dialog.slint Normal file
View File

@@ -0,0 +1,116 @@
// Dialog Component - Simplified version
// Modal dialog with backdrop and smooth animations
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
export component Dialog {
// Public properties
in-out property <bool> open: false;
in property <string> title: "";
in property <string> description: "";
// Callbacks
callback close();
width: 100%;
height: 100%;
// Only show when open
if open: Rectangle {
width: 100%;
height: 100%;
background: #00000080;
// Close on backdrop click
TouchArea {
clicked => {
root.close();
}
}
// Dialog content container (centered)
VerticalLayout {
alignment: center;
HorizontalLayout {
alignment: center;
// Dialog card
Rectangle {
width: 500px;
background: Theme.colors.card;
border-radius: SpacingSystem.radius.lg;
border-width: 1px;
border-color: Theme.colors.border;
drop-shadow-blur: 20px;
drop-shadow-color: #00000040;
VerticalLayout {
padding: SpacingSystem.spacing.s6;
spacing: SpacingSystem.spacing.s4;
// Header
HorizontalLayout {
spacing: SpacingSystem.spacing.s4;
alignment: space-between;
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
// Title
Text {
text: title;
font-size: Typography.sizes.xl;
font-weight: Typography.weights.semibold;
color: Theme.colors.card-foreground;
wrap: word-wrap;
}
// Description
Text {
text: description;
font-size: Typography.sizes.sm;
color: Theme.colors.muted-foreground;
wrap: word-wrap;
}
}
// Close button
Rectangle {
width: 32px;
height: 32px;
border-radius: SpacingSystem.radius.md;
background: close-touch.has-hover ? Theme.colors.muted : Colors.transparent;
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
Text {
text: "✕";
font-size: Typography.sizes.lg;
color: Theme.colors.muted-foreground;
horizontal-alignment: center;
vertical-alignment: center;
}
close-touch := TouchArea {
clicked => {
root.close();
}
}
}
}
}
// Prevent backdrop click from closing
TouchArea {
// Consume clicks to prevent backdrop from receiving them
}
}
}
}
}
}

74
ui/components/input.slint Normal file
View File

@@ -0,0 +1,74 @@
// Input Component
// Text input field with focus states and optional icons
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
export component Input {
// Public properties
in-out property <string> value: "";
in property <string> placeholder: "";
in property <bool> disabled: false;
in property <bool> error: false;
// Callbacks
callback edited(string);
callback accepted(string);
// Internal state
private property <bool> focused: false;
min-height: 40px;
// Main container
Rectangle {
background: Theme.colors.background;
border-radius: SpacingSystem.radius.md;
border-width: 1px;
border-color: error ? Theme.colors.destructive :
focused ? Theme.colors.ring :
Theme.colors.input;
// Smooth border color transition
animate border-color {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
HorizontalLayout {
padding: SpacingSystem.spacing.s2;
spacing: SpacingSystem.spacing.s2;
// Text input field
input-field := TextInput {
text <=> value;
enabled: !disabled;
font-size: Typography.sizes.base;
color: Theme.colors.foreground;
selection-background-color: Theme.colors.primary;
selection-foreground-color: Theme.colors.primary-foreground;
edited => {
root.edited(self.text);
}
accepted => {
root.accepted(self.text);
}
}
}
// Focus detection
FocusScope {
key-pressed(event) => {
focused = input-field.has-focus;
return accept;
}
key-released(event) => {
focused = input-field.has-focus;
return accept;
}
}
}
}

89
ui/components/item.slint Normal file
View File

@@ -0,0 +1,89 @@
// Item Component
// List item with icon, title, description and selection state
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
export component Item {
// Public properties
in property <string> icon: ""; // Text icon/emoji
in property <string> title;
in property <string> description: "";
in property <bool> selected: false;
// Callbacks
callback clicked();
// Internal state
private property <bool> hovered: false;
min-height: 60px;
// Main container
Rectangle {
background: selected ? Theme.colors.accent :
hovered ? Theme.colors.muted :
Colors.transparent;
border-radius: SpacingSystem.radius.md;
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
HorizontalLayout {
padding: SpacingSystem.spacing.s3;
spacing: SpacingSystem.spacing.s3;
alignment: start;
// Icon (if provided)
if icon != "": Rectangle {
width: 40px;
height: 40px;
border-radius: SpacingSystem.radius.md;
background: Theme.colors.muted;
Text {
text: icon;
font-size: Typography.sizes.xl;
horizontal-alignment: center;
vertical-alignment: center;
}
}
// Content area
VerticalLayout {
spacing: SpacingSystem.spacing.s1;
alignment: center;
// Title
Text {
text: title;
font-size: Typography.sizes.base;
font-weight: Typography.weights.medium;
color: Theme.colors.foreground;
wrap: word-wrap;
}
// Description (if provided)
if description != "": Text {
text: description;
font-size: Typography.sizes.sm;
color: Theme.colors.muted-foreground;
wrap: word-wrap;
}
}
}
// Touch interaction
TouchArea {
clicked => {
root.clicked();
}
moved => {
hovered = self.has-hover;
}
}
}
}

138
ui/components/sidebar.slint Normal file
View File

@@ -0,0 +1,138 @@
// Sidebar Component
// Collapsible navigation sidebar with menu items
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
// Navigation item structure
export struct NavItem {
icon: string, // Text icon/emoji
label: string,
active: bool,
}
export component Sidebar {
// Public properties
in-out property <bool> collapsed: false;
in property <[NavItem]> items: [];
// Callbacks
callback item-clicked(int); // Pass item index
callback toggle-collapsed();
// Calculate width based on collapsed state
private property <length> sidebar-width: collapsed ? 60px : 240px;
width: sidebar-width;
animate width {
duration: Animations.durations.normal;
easing: Animations.ease-in-out;
}
// Main container
Rectangle {
background: Theme.colors.card;
Rectangle {
x: parent.width - 1px;
width: 1px;
height: parent.height;
background: Theme.colors.border;
}
VerticalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s2;
// Toggle button
Rectangle {
height: 40px;
background: Colors.transparent;
border-radius: SpacingSystem.radius.md;
HorizontalLayout {
padding: SpacingSystem.spacing.s2;
spacing: SpacingSystem.spacing.s2;
Text {
text: collapsed ? "☰" : "✕";
font-size: Typography.sizes.xl;
color: Theme.colors.foreground;
horizontal-alignment: center;
vertical-alignment: center;
}
if !collapsed: Text {
text: "Menu";
font-size: Typography.sizes.base;
font-weight: Typography.weights.medium;
color: Theme.colors.foreground;
vertical-alignment: center;
}
}
TouchArea {
clicked => {
root.toggle-collapsed();
}
}
}
// Divider
Rectangle {
height: 1px;
background: Theme.colors.border;
}
// Navigation items
for item[index] in items: Rectangle {
height: 44px;
background: item.active ? Theme.colors.accent : Colors.transparent;
border-radius: SpacingSystem.radius.md;
states [
hovered when touch-area.has-hover && !item.active: {
background: Theme.colors.muted;
}
]
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
HorizontalLayout {
padding: SpacingSystem.spacing.s2;
spacing: SpacingSystem.spacing.s3;
alignment: start;
// Icon
Text {
text: item.icon;
font-size: Typography.sizes.xl;
color: item.active ? Theme.colors.accent-foreground : Theme.colors.foreground;
horizontal-alignment: center;
vertical-alignment: center;
width: 24px;
}
// Label (only when not collapsed)
if !collapsed: Text {
text: item.label;
font-size: Typography.sizes.base;
font-weight: item.active ? Typography.weights.medium : Typography.weights.normal;
color: item.active ? Theme.colors.accent-foreground : Theme.colors.foreground;
vertical-alignment: center;
}
}
touch-area := TouchArea {
clicked => {
root.item-clicked(index);
}
}
}
}
}
}

148
ui/components/toast.slint Normal file
View File

@@ -0,0 +1,148 @@
// Toast Component
// Notification toast with auto-dismiss and animations
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Animations } from "../utils/animations.slint";
// Toast message structure
export struct ToastMessage {
message: string,
variant: string, // default | success | error | warning
show: bool,
}
// Single toast item
export component Toast {
// Public properties
in property <string> message: "";
in property <string> variant: "default"; // default | success | error | warning
in-out property <bool> show: false;
// Callbacks
callback dismissed();
// Calculate colors based on variant
private property <color> bg-color: {
if variant == "success" { #10b981 }
else if variant == "error" { Theme.colors.destructive }
else if variant == "warning" { #f59e0b }
else { Theme.colors.card }
};
private property <color> text-color: {
if variant == "success" { #ffffff }
else if variant == "error" { Theme.colors.destructive-foreground }
else if variant == "warning" { #ffffff }
else { Theme.colors.card-foreground }
};
private property <string> icon: {
if variant == "success" { "✓" }
else if variant == "error" { "✕" }
else if variant == "warning" { "⚠" }
else { "" }
};
// Animation properties
property <length> translate-y: show ? 0px : -20px;
property <float> opacity-val: show ? 1.0 : 0.0;
animate translate-y, opacity-val {
duration: Animations.durations.normal;
easing: Animations.ease-out;
}
width: 350px;
height: show ? 60px : 0px;
if show: Rectangle {
y: translate-y;
opacity: opacity-val;
background: bg-color;
border-radius: SpacingSystem.radius.lg;
border-width: 1px;
border-color: Theme.colors.border;
drop-shadow-blur: 10px;
drop-shadow-color: #00000020;
drop-shadow-offset-y: 4px;
HorizontalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s3;
alignment: space-between;
HorizontalLayout {
spacing: SpacingSystem.spacing.s3;
// Icon
Text {
text: icon;
font-size: Typography.sizes.xl;
color: text-color;
vertical-alignment: center;
}
// Message
Text {
text: message;
font-size: Typography.sizes.base;
color: text-color;
vertical-alignment: center;
wrap: word-wrap;
}
}
// Close button
Rectangle {
width: 24px;
height: 24px;
border-radius: SpacingSystem.radius.sm;
background: close-touch.has-hover ? #00000020 : Colors.transparent;
animate background {
duration: Animations.durations.fast;
easing: Animations.ease-in-out;
}
Text {
text: "✕";
font-size: Typography.sizes.sm;
color: text-color;
horizontal-alignment: center;
vertical-alignment: center;
}
close-touch := TouchArea {
clicked => {
root.dismissed();
}
}
}
}
}
}
// Toast container (manages multiple toasts)
export component ToastContainer {
in property <[ToastMessage]> toasts: [];
callback toast-dismissed(int);
// Position at top-right corner
width: 370px;
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: start;
for toast[index] in toasts: Toast {
message: toast.message;
variant: toast.variant;
show: toast.show;
dismissed => {
root.toast-dismissed(index);
}
}
}
}

553
ui/demo.slint Normal file
View File

@@ -0,0 +1,553 @@
// Demo Application - Task Manager
// Showcases all shadcn-style UI components
import { Theme, Typography, SpacingSystem } from "theme/theme.slint";
import { Button } from "components/button.slint";
import { Input } from "components/input.slint";
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "components/card.slint";
import { Badge } from "components/badge.slint";
import { Item } from "components/item.slint";
import { Sidebar, NavItem } from "components/sidebar.slint";
import { Dialog } from "components/dialog.slint";
import { ToastContainer, ToastMessage } from "components/toast.slint";
export component Demo inherits Window {
title: "Slint Shadcn UI - Task Manager Demo";
background: Theme.colors.background;
// Window size settings - now resizable
min-width: 800px;
min-height: 600px;
preferred-width: 1400px;
preferred-height: 900px;
// Application state
in-out property <string> new-task-title: "";
in-out property <string> current-view: "tasks";
in-out property <bool> sidebar-collapsed: false;
in-out property <bool> dialog-open: false;
in-out property <[ToastMessage]> toasts: [];
// Sidebar navigation items
property <[NavItem]> nav-items: [
{ icon: "📝", label: "Tasks", active: current-view == "tasks" },
{ icon: "🎨", label: "Components", active: current-view == "components" },
{ icon: "⚙", label: "Settings", active: current-view == "settings" },
];
// Callbacks
callback add-task(string);
callback show-toast(string, string);
// Main layout
VerticalLayout {
// Top bar
Rectangle {
height: 60px;
background: Theme.colors.card;
Rectangle {
y: parent.height - 1px;
height: 1px;
background: Theme.colors.border;
}
HorizontalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s4;
alignment: space-between;
// Left: Title and badge
HorizontalLayout {
spacing: SpacingSystem.spacing.s3;
alignment: start;
Text {
text: "📋 Task Manager";
font-size: Typography.sizes.xl;
font-weight: Typography.weights.bold;
color: Theme.colors.foreground;
vertical-alignment: center;
}
Badge {
text: "Demo";
variant: "secondary";
}
}
// Right: Theme toggle button
Button {
text: Theme.is-dark-mode ? "☀ Light" : "🌙 Dark";
variant: "outline";
size: "sm";
clicked => {
Theme.toggle-theme();
}
}
}
}
// Main content area
HorizontalLayout {
// Sidebar
Sidebar {
items: nav-items;
collapsed: sidebar-collapsed;
item-clicked(index) => {
if index == 0 { current-view = "tasks"; }
else if index == 1 { current-view = "components"; }
else if index == 2 { current-view = "settings"; }
}
toggle-collapsed => {
sidebar-collapsed = !sidebar-collapsed;
}
}
// Main content area
Rectangle {
background: Theme.colors.background;
Flickable {
width: 100%;
height: 100%;
viewport-height: content-layout.preferred-height + 100px;
content-layout := VerticalLayout {
padding: SpacingSystem.spacing.s6;
spacing: SpacingSystem.spacing.s6;
// Tasks view
if current-view == "tasks": VerticalLayout {
spacing: SpacingSystem.spacing.s6;
// Add task card
Card {
CardHeader {
CardTitle { text: "Add New Task"; }
CardDescription { text: "Create a new task to track your work"; }
}
CardContent {
Input {
value <=> new-task-title;
placeholder: "Enter task title...";
accepted(text) => {
if text != "" {
root.add-task(text);
}
}
}
}
CardFooter {
Button {
text: "Add Task";
clicked => {
if new-task-title != "" {
root.add-task(new-task-title);
}
}
}
Button {
text: "Clear";
variant: "outline";
clicked => {
new-task-title = "";
}
}
}
}
// Example task 1
Card {
hoverable: true;
CardHeader {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: space-between;
CardTitle { text: "Design UI Components"; }
Badge { text: "In Progress"; variant: "default"; }
}
}
CardContent {
CardDescription {
text: "Create a comprehensive set of UI components following shadcn design principles with proper theming support.";
}
}
CardFooter {
Button {
text: "Complete";
size: "sm";
clicked => {
root.show-toast("Task completed!", "success");
}
}
Button {
text: "Delete";
variant: "destructive";
size: "sm";
clicked => {
dialog-open = true;
}
}
}
}
// Example task 2
Card {
hoverable: true;
CardHeader {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: space-between;
CardTitle { text: "Write Documentation"; }
Badge { text: "Todo"; variant: "secondary"; }
}
}
CardContent {
CardDescription {
text: "Document all components with usage examples and API references for developers.";
}
}
CardFooter {
Button {
text: "Start";
variant: "outline";
size: "sm";
clicked => {
root.show-toast("Task started!", "default");
}
}
}
}
// Example task 3
Card {
hoverable: true;
CardHeader {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: space-between;
CardTitle { text: "Test Components"; }
Badge { text: "Done"; variant: "outline"; }
}
}
CardContent {
CardDescription {
text: "Thoroughly test all components across different themes and screen sizes.";
}
}
CardFooter {
Button {
text: "View Details";
variant: "ghost";
size: "sm";
}
}
}
}
// Components showcase view
if current-view == "components": VerticalLayout {
spacing: SpacingSystem.spacing.s6;
// Button showcase
Card {
CardHeader {
CardTitle { text: "Button Component"; }
CardDescription { text: "Various button styles and sizes"; }
}
CardContent {
VerticalLayout {
spacing: SpacingSystem.spacing.s4;
// Variants
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Button { text: "Default"; }
Button { text: "Secondary"; variant: "secondary"; }
Button { text: "Outline"; variant: "outline"; }
Button { text: "Ghost"; variant: "ghost"; }
Button { text: "Destructive"; variant: "destructive"; }
}
// Sizes
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: center;
Button { text: "Small"; size: "sm"; }
Button { text: "Medium"; size: "md"; }
Button { text: "Large"; size: "lg"; }
}
// Disabled
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Button { text: "Disabled"; disabled: true; }
}
}
}
}
// Badge showcase
Card {
CardHeader {
CardTitle { text: "Badge Component"; }
CardDescription { text: "Status indicators and labels"; }
}
CardContent {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Badge { text: "Default"; }
Badge { text: "Secondary"; variant: "secondary"; }
Badge { text: "Destructive"; variant: "destructive"; }
Badge { text: "Outline"; variant: "outline"; }
}
}
}
// Input showcase
Card {
CardHeader {
CardTitle { text: "Input Component"; }
CardDescription { text: "Text input fields with various states"; }
}
CardContent {
VerticalLayout {
spacing: SpacingSystem.spacing.s4;
Input { placeholder: "Normal input..."; }
Input { placeholder: "Disabled input..."; disabled: true; }
Input { placeholder: "Error input..."; error: true; }
}
}
}
// Item showcase
Card {
CardHeader {
CardTitle { text: "Item Component"; }
CardDescription { text: "List items with icons and descriptions"; }
}
CardContent {
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
Item {
icon: "📁";
title: "Project Files";
description: "All your project documents and resources";
}
Item {
icon: "⭐";
title: "Favorites";
description: "Your starred items for quick access";
selected: true;
}
Item {
icon: "🗑";
title: "Trash";
description: "Recently deleted items";
}
}
}
}
// Dialog and Toast showcase
Card {
CardHeader {
CardTitle { text: "Dialog & Toast"; }
CardDescription { text: "Modal dialogs and notification toasts"; }
}
CardContent {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Button {
text: "Open Dialog";
clicked => {
dialog-open = true;
}
}
Button {
text: "Show Success Toast";
variant: "outline";
clicked => {
root.show-toast("Operation successful!", "success");
}
}
Button {
text: "Show Error Toast";
variant: "outline";
clicked => {
root.show-toast("Something went wrong!", "error");
}
}
Button {
text: "Show Warning Toast";
variant: "outline";
clicked => {
root.show-toast("Please be careful!", "warning");
}
}
}
}
}
}
// Settings view
if current-view == "settings": VerticalLayout {
spacing: SpacingSystem.spacing.s6;
Card {
CardHeader {
CardTitle { text: "Appearance"; }
CardDescription { text: "Customize the look and feel of the application"; }
}
CardContent {
VerticalLayout {
spacing: SpacingSystem.spacing.s4;
HorizontalLayout {
spacing: SpacingSystem.spacing.s4;
alignment: space-between;
VerticalLayout {
spacing: SpacingSystem.spacing.s1;
Text {
text: "Theme";
font-size: Typography.sizes.base;
font-weight: Typography.weights.medium;
color: Theme.colors.foreground;
}
Text {
text: "Choose between light and dark mode";
font-size: Typography.sizes.sm;
color: Theme.colors.muted-foreground;
}
}
Button {
text: Theme.is-dark-mode ? "Dark Mode" : "Light Mode";
variant: "outline";
clicked => {
Theme.toggle-theme();
}
}
}
}
}
}
Card {
CardHeader {
CardTitle { text: "About"; }
CardDescription { text: "Information about this demo application"; }
}
CardContent {
VerticalLayout {
spacing: SpacingSystem.spacing.s3;
Text {
text: "Slint Shadcn UI Component Library";
font-size: Typography.sizes.base;
font-weight: Typography.weights.semibold;
color: Theme.colors.foreground;
}
Text {
text: "A comprehensive set of UI components inspired by shadcn/ui, built with Slint. Features include theme support, smooth animations, and a clean, modern design.";
font-size: Typography.sizes.sm;
color: Theme.colors.muted-foreground;
wrap: word-wrap;
}
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Badge { text: "v1.0.0"; variant: "secondary"; }
Badge { text: "Slint 1.14"; variant: "outline"; }
}
}
}
}
}
}
}
}
}
}
// Dialog overlay - inline implementation
Dialog {
open: dialog-open;
title: "Confirm Delete";
description: "Are you sure you want to delete this task? This action cannot be undone.";
close => {
dialog-open = false;
}
}
// Toast container
Rectangle {
x: parent.width - 390px;
y: 20px;
width: 370px;
ToastContainer {
toasts: toasts;
toast-dismissed(index) => {
// Will be handled by Rust
}
}
}
}

75
ui/theme/colors.slint Normal file
View File

@@ -0,0 +1,75 @@
// Shadcn-style Color System
// Based on shadcn/ui zinc theme with light/dark mode support
// Color palette structure
export struct ColorPalette {
background: color,
foreground: color,
card: color,
card-foreground: color,
popover: color,
popover-foreground: color,
primary: color,
primary-foreground: color,
secondary: color,
secondary-foreground: color,
muted: color,
muted-foreground: color,
accent: color,
accent-foreground: color,
destructive: color,
destructive-foreground: color,
border: color,
input: color,
ring: color,
}
// Light mode colors - shadcn zinc theme
export global LightColors {
out property <ColorPalette> palette: {
background: #ffffff,
foreground: #09090b,
card: #ffffff,
card-foreground: #09090b,
popover: #ffffff,
popover-foreground: #09090b,
primary: #18181b,
primary-foreground: #fafafa,
secondary: #f4f4f5,
secondary-foreground: #18181b,
muted: #f4f4f5,
muted-foreground: #71717a,
accent: #f4f4f5,
accent-foreground: #18181b,
destructive: #ef4444,
destructive-foreground: #fafafa,
border: #e4e4e7,
input: #e4e4e7,
ring: #18181b,
};
}
// Dark mode colors - shadcn zinc theme
export global DarkColors {
out property <ColorPalette> palette: {
background: #09090b,
foreground: #fafafa,
card: #09090b,
card-foreground: #fafafa,
popover: #09090b,
popover-foreground: #fafafa,
primary: #fafafa,
primary-foreground: #18181b,
secondary: #27272a,
secondary-foreground: #fafafa,
muted: #27272a,
muted-foreground: #a1a1aa,
accent: #27272a,
accent-foreground: #fafafa,
destructive: #7f1d1d,
destructive-foreground: #fafafa,
border: #27272a,
input: #27272a,
ring: #d4d4d8,
};
}

55
ui/theme/spacing.slint Normal file
View File

@@ -0,0 +1,55 @@
// Spacing and Border Radius System
// Consistent spacing scale following shadcn design principles
// Spacing scale (px)
export struct Spacing {
s0: length,
s1: length,
s2: length,
s3: length,
s4: length,
s6: length,
s8: length,
s12: length,
s16: length,
s24: length,
s32: length,
}
// Border radius scale (px)
export struct BorderRadius {
sm: length,
md: length,
lg: length,
xl: length,
xl2: length,
full: length,
}
// Global spacing configuration
export global SpacingSystem {
// Spacing values
out property <Spacing> spacing: {
s0: 0px,
s1: 4px,
s2: 8px,
s3: 12px,
s4: 16px,
s6: 24px,
s8: 32px,
s12: 48px,
s16: 64px,
s24: 96px,
s32: 128px,
};
// Border radius values
out property <BorderRadius> radius: {
sm: 4px,
md: 6px,
lg: 8px,
xl: 12px,
xl2: 16px,
full: 9999px,
};
}

26
ui/theme/theme.slint Normal file
View File

@@ -0,0 +1,26 @@
// Theme Manager
// Global theme state with light/dark mode support
import { ColorPalette, LightColors, DarkColors } from "colors.slint";
import { Typography } from "typography.slint";
import { SpacingSystem } from "spacing.slint";
// Global theme manager (singleton)
export global Theme {
// Theme state
in-out property <bool> is-dark-mode: false;
// Current active color palette (reactive)
out property <ColorPalette> colors: is-dark-mode ?
DarkColors.palette : LightColors.palette;
// Theme toggle callback
callback toggle-theme();
toggle-theme => {
is-dark-mode = !is-dark-mode;
}
}
// Re-export for convenience
export { Typography, SpacingSystem }

59
ui/theme/typography.slint Normal file
View File

@@ -0,0 +1,59 @@
// Typography System
// Font sizes, weights, and line heights following shadcn design principles
// Font size scale
export struct FontSizes {
xs: length,
sm: length,
base: length,
lg: length,
xl: length,
xl2: length,
xl3: length,
xl4: length,
}
// Font weight scale
export struct FontWeights {
normal: int,
medium: int,
semibold: int,
bold: int,
}
// Line height scale
export struct LineHeights {
tight: float,
normal: float,
relaxed: float,
}
// Global typography configuration
export global Typography {
// Font sizes (px)
out property <FontSizes> sizes: {
xs: 12px,
sm: 14px,
base: 16px,
lg: 18px,
xl: 20px,
xl2: 24px,
xl3: 30px,
xl4: 36px,
};
// Font weights
out property <FontWeights> weights: {
normal: 400,
medium: 500,
semibold: 600,
bold: 700,
};
// Line heights (relative)
out property <LineHeights> line-heights: {
tight: 1.25,
normal: 1.5,
relaxed: 1.75,
};
}

25
ui/utils/animations.slint Normal file
View File

@@ -0,0 +1,25 @@
// Animation System
// Standard animation durations and easing functions
// Animation duration scale
export struct AnimationDurations {
fast: duration,
normal: duration,
slow: duration,
}
// Global animation configuration
export global Animations {
// Duration presets
out property <AnimationDurations> durations: {
fast: 150ms,
normal: 200ms,
slow: 300ms,
};
// Easing functions (cubic-bezier)
out property <easing> ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
out property <easing> ease-in: cubic-bezier(0.4, 0, 1, 1);
out property <easing> ease-out: cubic-bezier(0, 0, 0.2, 1);
out property <easing> ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}