A TailwindCSS + DaisyUI inspired component library and scaffold for Gio UI — Go's immediate-mode cross-platform GUI framework.
Naming follows Tailwind / DaisyUI conventions so that if you know Tailwind's flex flex-col gap-4 p-6 rounded-lg or DaisyUI's btn btn-primary, you'll feel right at home in Go.
gioui-kit/
├──
│ ├── theme/ # 🎨 Color palette + semantic tokens + typography
│ │ └── theme.go # Tailwind colors (Slate50..950, Blue500, etc.)
│ │ # DaisyUI tokens (Primary, Secondary, Base100, etc.)
│ │ # Preset themes: Light(), Dark(), Cupcake(), Nord()
│ │
│ ├── layout/ # 📐 Flexbox, Grid, Container, Spacing utilities
│ │ └── layout.go # FlexRow, FlexCol with Gap support
│ │ # Grid{Cols: 3, Gap: 16}
│ │ # P(), Px(), Py(), W(), H(), MinW(), MaxW()
│ │ # Container, Box, DividerH, ScrollY/ScrollX
│ │
│ ├── modifier/ # ✨ Visual decorators (shadow, gradient, ring, bg)
│ │ └── modifier.go# Shadow (ShadowSm..Shadow2xl)
│ │ # LinearGradient, Bg, Rounded, Ring, Opacity
│ │
│ ├── component/ # 🧩 DaisyUI components
│ │ └── component.go# Button (variants + sizes)
│ │ # Badge, Card, Input, Toggle, Alert
│ │ # Avatar, Progress, Tabs, Chip, Skeleton, Text
│ │
│ └── scaffold/ # 🏗️ App-level layout scaffolds
│ └── scaffold.go# AppShell (Navbar + Sidebar + Content)
│ # Navbar, Sidebar, Modal, Drawer
│ # BottomNav, Toast, Breadcrumb
│
├── cmd/demo/ # 🚀 Example application
│ └── main.go
│
├── go.mod
└── README.md
package main
import (
"gioui.org/app"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/widget"
"github.com/ossprovider/gioui-kit/component"
kit "github.com/ossprovider/gioui-kit/layout"
"github.com/ossprovider/gioui-kit/theme"
)
func main() {
th := theme.Light()
var btnClick widget.Clickable
// ... inside your frame loop:
// A card with a button inside a padded flex column
kit.FlexCol{Gap: 16}.Layout(gtx,
kit.Rigid(func(gtx layout.Context) layout.Dimensions {
return component.NewCard(th).WithBorder().CardWithHeader(gtx, "Welcome",
func(gtx layout.Context) layout.Dimensions {
return component.NewButton(th, &btnClick, "Get Started").
WithVariant(component.BtnPrimary).
Layout(gtx)
},
)
}),
)
}All Tailwind colors are exported as color.NRGBA variables:
// Gray scales
theme.Slate50 ... theme.Slate950
theme.Gray50 ... theme.Gray950
theme.Zinc50 ... theme.Zinc950
// Chromatic colors
theme.Red50 ... theme.Red900
theme.Blue50 ... theme.Blue900
theme.Green50 ... theme.Green900
theme.Yellow50 ... theme.Yellow900
theme.Purple50 ... theme.Purple900
theme.Indigo50 ... theme.Indigo900
theme.Amber50 ... theme.Amber900
theme.Cyan50 ... theme.Cyan900
theme.Emerald50... theme.Emerald900
theme.Rose50 ... theme.Rose900
// Utilities
theme.White, theme.Black, theme.Transparent
theme.WithAlpha(c, 128) // set alpha
theme.Opacity(c, 0.5) // multiply alpha
theme.Lerp(a, b, 0.5) // interpolate
theme.RGB(0xff6600) // create from hexth := theme.Light() // or Dark(), Cupcake(), Nord()
th.Primary // Main brand color
th.PrimaryContent // Text on Primary
th.Secondary // Secondary brand
th.Accent // Accent / highlight
th.Neutral // Neutral dark
th.Info / Success / Warning / Error // State colors
th.Base100 // Background (lightest)
th.Base200 // Background (medium)
th.Base300 // Background (darkest) / border
th.BaseContent // Text on base backgrounds
// Typography scale
th.FontSize // 16sp base
th.H1Size .. th.XsSize
// Spacing scale (Tailwind-like)
th.Space0 (0dp) .. th.Space16 (64dp)
// Border radius
th.RoundedNone (0) .. th.RoundedFull (9999)// Horizontal flex with gap (like `flex flex-row gap-4 items-center`)
kit.FlexRow{
Gap: 16, // gap-4
Alignment: kit.ItemsCenter, // items-center
Spacing: kit.JustifyBetween, // justify-between
}.Layout(gtx,
kit.Rigid(widgetA), // flex child
kit.Grow(widgetB), // flex-1
kit.Flexed(0.5, widgetC), // flex-grow: 0.5
)
// Vertical flex
kit.FlexCol{Gap: 8}.Layout(gtx, children...)| Tailwind Class | GioUI Kit Constant |
|---|---|
items-start |
kit.ItemsStart |
items-center |
kit.ItemsCenter |
items-end |
kit.ItemsEnd |
justify-start |
kit.JustifyStart |
justify-center |
kit.JustifyCenter |
justify-between |
kit.JustifyBetween |
justify-around |
kit.JustifyAround |
// 3-column grid with gap (like `grid grid-cols-3 gap-4`)
kit.Grid{Cols: 3, Gap: 16}.Layout(gtx,
widgetA, widgetB, widgetC,
widgetD, widgetE,
)// Box (like <div class="p-4 bg-white rounded-lg border">)
kit.Box{
Padding: kit.P(16),
Background: th.Base100,
Radius: th.RoundedLg,
Border: kit.Border{Color: th.Base300, Width: 1},
MaxWidth: 400,
}.Layout(gtx, content)
// Centered max-width container (like `container mx-auto px-4`)
kit.Container{MaxWidth: 1280, Padding: 16}.Layout(gtx, content)kit.P(16) // p-4 uniform padding
kit.Px(16) // px-4 horizontal padding
kit.Py(8) // py-2 vertical padding
kit.Pt(12) // pt-3 top padding
kit.Pb(12) // pb-3 bottom padding
kit.Pl(8) // pl-2 left padding
kit.Pr(8) // pr-2 right padding
kit.Inset4(t, r, b, l) // custom all sides
kit.SpaceH(16) // horizontal spacer widget
kit.SpaceV(16) // vertical spacer widgetkit.W(200)(gtx, widget) // w-[200px] fixed width
kit.H(100)(gtx, widget) // h-[100px] fixed height
kit.MinW(300)(gtx, widget) // min-w-[300px]
kit.MaxW(600)(gtx, widget) // max-w-[600px]
kit.WFull(gtx, widget) // w-fullscroll := kit.NewScrollY()
scroll.Layout(gtx, len(items), func(gtx layout.Context, i int) layout.Dimensions {
return renderItem(gtx, items[i])
})// Shadow (like `shadow-lg rounded-xl`)
modifier.Shadow{
Style: modifier.ShadowLg,
Radius: th.RoundedXl,
}.Layout(gtx, content)
// Available presets: ShadowSm, ShadowMd, ShadowLg, ShadowXl, Shadow2xl
// Background with radius
modifier.Bg{Color: th.Primary, Radius: th.RoundedLg}.Layout(gtx, content)
// Clip to rounded shape
modifier.Rounded{Radius: th.RoundedFull}.Layout(gtx, content)
// Focus ring (like `ring-2 ring-blue-500 ring-offset-2`)
modifier.Ring{
Width: 2,
Color: th.Primary,
Offset: 2,
Radius: th.RoundedLg,
}.Layout(gtx, content)
// Linear gradient background
modifier.LinearGradient{
From: th.Primary,
To: th.Secondary,
Dir: modifier.GradientToRight,
Radius: th.RoundedLg,
}.Layout(gtx, content)btn := component.NewButton(th, &clickable, "Click Me").
WithVariant(component.BtnPrimary). // BtnSecondary, BtnAccent, BtnGhost, ...
WithSize(component.BtnLg) // BtnXs, BtnSm, BtnMd, BtnLg
btn.Disabled = true
btn.Loading = true
btn.Layout(gtx)| Variant | Description |
|---|---|
BtnDefault |
Neutral background |
BtnPrimary |
Brand primary color |
BtnSecondary |
Secondary color |
BtnAccent |
Accent color |
BtnGhost |
Transparent bg |
BtnLink |
Text link style |
BtnOutline |
Bordered, no fill |
BtnInfo/Success/Warning/Error |
State colors |
component.NewBadge(th, "NEW").WithVariant(component.BadgePrimary).Layout(gtx)
// Variants: BadgeDefault, BadgePrimary, BadgeSecondary, BadgeAccent,
// BadgeSuccess, BadgeWarning, BadgeError, BadgeGhostcard := component.NewCard(th).WithBorder().WithCompact()
card.Layout(gtx, content)
card.CardWithHeader(gtx, "Title", content)component.NewAlert(th, "Success!", component.AlertSuccess).Layout(gtx)
// Variants: AlertInfo, AlertSuccess, AlertWarning, AlertErrorinput := component.NewInput(th, &editor, "Placeholder...").
WithLabel("Email").
WithVariant(component.InputPrimary)
input.Layout(gtx)
// Variants: InputBordered, InputGhost, InputPrimary, InputError, ...
// Sizes: InputXs, InputSm, InputMd, InputLgcomponent.NewToggle(th, &boolState, "Enable feature").Layout(gtx)avatar := component.NewAvatar(th, "HS")
avatar.Size = component.AvatarLg
avatar.Layout(gtx)p := component.NewProgress(th, 0.65)
p.Variant = component.ProgressSuccess
p.Layout(gtx)tabs := component.NewTabs(th, []string{"Tab 1", "Tab 2", "Tab 3"})
tabs.Layout(gtx)
selected := tabs.Selectedcomponent.NewText(th, "Hello World").H1().Bold().WithColor(th.Primary).Layout(gtx)
// Sizes: .H1(), .H2(), .H3(), .H4(), .Sm(), .Xs()
// Weight: .Bold()component.NewChip(th, "Go").Layout(gtx)
component.NewSkeleton(th).Layout(gtx)shell := scaffold.NewAppShell(th).
WithNavbar(navbarWidget).
WithSidebar(sidebarWidget, 256).
WithContent(contentWidget)
shell.Layout(gtx)navbar := scaffold.NewNavbar(th)
navbar.Layout(gtx,
startWidget, // left section (brand/logo)
centerWidget, // center section
endWidget, // right section (actions)
)items := []scaffold.SidebarItem{
{Label: "Dashboard", Icon: "◉", Active: true},
{Label: "Settings", Icon: "⚙"},
}
sidebar := scaffold.NewSidebar(th, items)
sidebar.OnSelect = func(i int) { /* handle */ }
sidebar.Layout(gtx)modal := scaffold.NewModal(th)
modal.Show() // or modal.Hide()
modal.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return dialogContent(gtx)
})drawer := scaffold.NewDrawer(th)
drawer.Side = scaffold.DrawerLeft // or DrawerRight
drawer.Open() // .Close(), .Toggle()
drawer.Layout(gtx, drawerContent)nav := scaffold.NewBottomNav(th, []scaffold.BottomNavItem{
{Label: "Home", Icon: "⌂", Active: true},
{Label: "Search", Icon: "🔍"},
})
nav.Layout(gtx)toast := scaffold.NewToast(th)
toast.Show("Saved!")
toast.Position = scaffold.ToastBottomRight
toast.Layout(gtx)scaffold.NewBreadcrumb(th, "Home", "Products", "Detail").Layout(gtx)GioUI Kit includes a Tailwind-style breakpoint system based on the available
widget width (gtx.Constraints.Max.X).
| Constant | Min width | Tailwind prefix |
|---|---|---|
BreakpointXs |
— | (default/mobile) |
BreakpointSm |
640 dp | sm: |
BreakpointMd |
768 dp | md: |
BreakpointLg |
1024 dp | lg: |
BreakpointXl |
1280 dp | xl: |
Breakpoint2xl |
1536 dp | 2xl: |
bp := kit.ScreenBreakpoint(gtx) // current breakpoint
w := kit.ScreenWidthDp(gtx) // available width in dp (float32)
// Conditional layout
if bp < kit.BreakpointMd {
// mobile layout
} else {
// desktop layout
}// 1 col on mobile → 2 on tablet → 3 on desktop (like Tailwind grid-cols-1 md:grid-cols-2 lg:grid-cols-3)
kit.Grid{
Cols: 1,
MdCols: 2,
LgCols: 3,
Gap: 16,
}.Layout(gtx, widgetA, widgetB, widgetC, widgetD)The sidebar is automatically hidden on narrow screens:
shell := scaffold.NewAppShell(th).
WithNavbar(navbarWidget).
WithSidebar(sidebarWidget, 256).
WithContent(contentWidget)
// Sidebar visible at ≥ 1024 dp by default.
// Override:
shell.HideSidebarBelow = kit.BreakpointMd // hide below 768 dp
shell.HideSidebarBelow = kit.BreakpointXs // always show sidebar
shell.Layout(gtx)On mobile (sidebar hidden), pair with a Drawer to provide sidebar access:
// Show a hamburger button in the Navbar that opens the Drawer
drawer := scaffold.NewDrawer(th)
drawer.Side = scaffold.DrawerLeft
// ... in navbar end widget:
if hamburgerBtn.Clicked(gtx) {
drawer.Toggle()
}
drawer.Layout(gtx, sidebarWidget)| Tailwind CSS | GioUI Kit |
|---|---|
flex flex-row gap-4 |
kit.FlexRow{Gap: 16} |
flex flex-col gap-2 |
kit.FlexCol{Gap: 8} |
items-center |
Alignment: kit.ItemsCenter |
justify-between |
Spacing: kit.JustifyBetween |
flex-1 |
kit.Grow(widget) |
grid grid-cols-3 gap-4 |
kit.Grid{Cols: 3, Gap: 16} |
p-4 |
kit.P(16) |
px-6 py-2 |
kit.Px(24) + kit.Py(8) |
w-full |
kit.WFull |
max-w-lg |
kit.MaxW(512) |
bg-white rounded-lg shadow-md |
modifier.Shadow{ShadowMd, RoundedLg} + Bg{} |
text-sm font-bold text-gray-500 |
NewText(th, "...").Sm().Bold().WithColor(Gray500) |
btn btn-primary btn-lg |
NewButton(th, &c, "Go").WithVariant(BtnPrimary).WithSize(BtnLg) |
badge badge-accent |
NewBadge(th, "Tag").WithVariant(BadgeAccent) |
card card-bordered |
NewCard(th).WithBorder() |
alert alert-success |
NewAlert(th, "OK!", AlertSuccess) |
input input-bordered |
NewInput(th, &ed, "...").WithVariant(InputBordered) |
toggle |
NewToggle(th, &b, "Label") |
cd cmd/demo
go run .MIT