├── .dockerignore
├── .env.example
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── question.md
└── workflows
│ └── validate-html.yml
├── .gitignore
├── .vscode
└── launch.json
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── assets
├── assets.go
├── css
│ ├── input.css
│ └── themes.css
├── img
│ ├── aspect_ratio_placeholder.jpeg
│ ├── card_placeholder.jpeg
│ ├── demo
│ │ ├── carousel-1.jpeg
│ │ ├── carousel-2.jpeg
│ │ └── carousel-3.jpeg
│ ├── favicon.svg
│ ├── gopher.svg
│ ├── readme.png
│ ├── social-preview.png
│ ├── tailwindcss-mark.svg
│ └── templ-mark.svg
└── js
│ └── templui.min.js
├── cmd
├── docs
│ └── main.go
├── icongen
│ └── main.go
├── render-showcases
│ └── main.go
├── sitemap
│ └── main.go
└── templui
│ └── main.go
├── docker-compose.yml
├── eslint.config.js
├── go.mod
├── go.sum
├── internal
├── components
│ ├── accordion
│ │ └── accordion.templ
│ ├── alert
│ │ └── alert.templ
│ ├── aspectratio
│ │ └── aspectratio.templ
│ ├── avatar
│ │ ├── avatar.js
│ │ ├── avatar.min.js
│ │ └── avatar.templ
│ ├── badge
│ │ └── badge.templ
│ ├── breadcrumb
│ │ └── breadcrumb.templ
│ ├── button
│ │ └── button.templ
│ ├── calendar
│ │ ├── calendar.js
│ │ ├── calendar.min.js
│ │ └── calendar.templ
│ ├── card
│ │ └── card.templ
│ ├── carousel
│ │ ├── carousel.js
│ │ ├── carousel.min.js
│ │ └── carousel.templ
│ ├── chart
│ │ ├── chart.js
│ │ ├── chart.min.js
│ │ ├── chart.templ
│ │ └── chartjs.js
│ ├── checkbox
│ │ └── checkbox.templ
│ ├── checkboxcard
│ │ └── checkboxcard.templ
│ ├── code
│ │ ├── code.js
│ │ ├── code.min.js
│ │ ├── code.templ
│ │ └── highlight.js
│ ├── datepicker
│ │ ├── datepicker.js
│ │ ├── datepicker.min.js
│ │ └── datepicker.templ
│ ├── drawer
│ │ ├── drawer.js
│ │ ├── drawer.min.js
│ │ └── drawer.templ
│ ├── dropdown
│ │ ├── dropdown.js
│ │ ├── dropdown.min.js
│ │ └── dropdown.templ
│ ├── embed.go
│ ├── form
│ │ └── form.templ
│ ├── icon
│ │ ├── icon.go
│ │ ├── icondata.go
│ │ └── icondefs.go
│ ├── input
│ │ ├── input.js
│ │ ├── input.min.js
│ │ └── input.templ
│ ├── inputotp
│ │ ├── inputotp.js
│ │ ├── inputotp.min.js
│ │ └── inputotp.templ
│ ├── label
│ │ ├── label.js
│ │ ├── label.min.js
│ │ └── label.templ
│ ├── main.js
│ ├── modal
│ │ ├── modal.js
│ │ ├── modal.min.js
│ │ └── modal.templ
│ ├── pagination
│ │ └── pagination.templ
│ ├── popover
│ │ ├── floating_ui_core.js
│ │ ├── floating_ui_dom.js
│ │ ├── popover.js
│ │ ├── popover.min.js
│ │ └── popover.templ
│ ├── progress
│ │ ├── progress.js
│ │ ├── progress.min.js
│ │ └── progress.templ
│ ├── radio
│ │ └── radio.templ
│ ├── radiocard
│ │ └── radiocard.templ
│ ├── rating
│ │ ├── rating.js
│ │ ├── rating.min.js
│ │ └── rating.templ
│ ├── selectbox
│ │ ├── selectbox.js
│ │ ├── selectbox.min.js
│ │ └── selectbox.templ
│ ├── separator
│ │ └── separator.templ
│ ├── skeleton
│ │ └── skeleton.templ
│ ├── slider
│ │ ├── slider.js
│ │ ├── slider.min.js
│ │ └── slider.templ
│ ├── spinner
│ │ └── spinner.templ
│ ├── table
│ │ └── table.templ
│ ├── tabs
│ │ ├── tabs.js
│ │ ├── tabs.min.js
│ │ └── tabs.templ
│ ├── textarea
│ │ ├── textarea.js
│ │ ├── textarea.min.js
│ │ └── textarea.templ
│ ├── timepicker
│ │ └── timepicker.templ
│ ├── toast
│ │ ├── toast.js
│ │ ├── toast.min.js
│ │ └── toast.templ
│ ├── toggle
│ │ └── toggle.templ
│ └── tooltip
│ │ └── tooltip.templ
├── config
│ └── config.go
├── ctxkeys
│ └── ctxkeys.go
├── manifest.json
├── middleware
│ └── middleware.go
├── shared
│ └── menudata.go
├── ui
│ ├── icons
│ │ ├── github.templ
│ │ ├── js.templ
│ │ ├── tailwind.templ
│ │ ├── templui.templ
│ │ └── x.templ
│ ├── layouts
│ │ ├── base.templ
│ │ └── docs.templ
│ ├── modules
│ │ ├── announcement_bar.templ
│ │ ├── code.templ
│ │ ├── codesnippet_embed.templ
│ │ ├── component_usage.templ
│ │ ├── container_wrapper.templ
│ │ ├── example_wrapper.templ
│ │ ├── footer.templ
│ │ ├── navbar.templ
│ │ ├── page_wrapper.templ
│ │ ├── seo.templ
│ │ ├── sidebar.templ
│ │ ├── table_of_contents.templ
│ │ └── themeswitcher.templ
│ ├── pages
│ │ ├── accordion.templ
│ │ ├── alert.templ
│ │ ├── aspect_ratio.templ
│ │ ├── avatar.templ
│ │ ├── badge.templ
│ │ ├── breadcrumb.templ
│ │ ├── button.templ
│ │ ├── calendar.templ
│ │ ├── card.templ
│ │ ├── carousel.templ
│ │ ├── chart.templ
│ │ ├── checkbox.templ
│ │ ├── checkbox_card.templ
│ │ ├── code.templ
│ │ ├── date_picker.templ
│ │ ├── drawer.templ
│ │ ├── dropdown.templ
│ │ ├── form.templ
│ │ ├── how_to_use.templ
│ │ ├── icon.templ
│ │ ├── input-otp.templ
│ │ ├── input.templ
│ │ ├── introduction.templ
│ │ ├── label.templ
│ │ ├── landing.templ
│ │ ├── modal.templ
│ │ ├── pagination.templ
│ │ ├── popover.templ
│ │ ├── progress.templ
│ │ ├── radio.templ
│ │ ├── radio_card.templ
│ │ ├── rating.templ
│ │ ├── select_box.templ
│ │ ├── separator.templ
│ │ ├── skeleton.templ
│ │ ├── slider.templ
│ │ ├── spinner.templ
│ │ ├── table.templ
│ │ ├── tabs.templ
│ │ ├── textarea.templ
│ │ ├── themes.templ
│ │ ├── time_picker.templ
│ │ ├── toast.templ
│ │ ├── toggle.templ
│ │ └── tooltip.templ
│ └── showcase
│ │ ├── accordion_default.templ
│ │ ├── alert_default.templ
│ │ ├── alert_destructive.templ
│ │ ├── aspect_ratio_default.templ
│ │ ├── avatar_default.templ
│ │ ├── avatar_fallback.templ
│ │ ├── avatar_group.templ
│ │ ├── avatar_sizes.templ
│ │ ├── avatar_with_icon.templ
│ │ ├── badge_default.templ
│ │ ├── badge_destructive.templ
│ │ ├── badge_outline.templ
│ │ ├── badge_secondary.templ
│ │ ├── badge_with_icon.templ
│ │ ├── breadcrumb_custom_separator.templ
│ │ ├── breadcrumb_default.templ
│ │ ├── breadcrumb_responsive.templ
│ │ ├── breadcrumb_with_icons.templ
│ │ ├── button_default.templ
│ │ ├── button_destructive.templ
│ │ ├── button_ghost.templ
│ │ ├── button_htmx_loading.templ
│ │ ├── button_icon.templ
│ │ ├── button_link.templ
│ │ ├── button_loading.templ
│ │ ├── button_outline.templ
│ │ ├── button_primary.templ
│ │ ├── button_secondary.templ
│ │ ├── button_with_icon.templ
│ │ ├── calendar_default.templ
│ │ ├── card_default.templ
│ │ ├── card_image_bottom.templ
│ │ ├── card_image_left.templ
│ │ ├── card_image_right.templ
│ │ ├── card_image_top.templ
│ │ ├── carousel_autoplay.templ
│ │ ├── carousel_default.templ
│ │ ├── carousel_minimal.templ
│ │ ├── carousel_with_images.templ
│ │ ├── chart_area.templ
│ │ ├── chart_area_linear.templ
│ │ ├── chart_area_stacked.templ
│ │ ├── chart_area_step.templ
│ │ ├── chart_bar_horizontal.templ
│ │ ├── chart_bar_multiple.templ
│ │ ├── chart_bar_negative.templ
│ │ ├── chart_bar_stacked.templ
│ │ ├── chart_default.templ
│ │ ├── chart_doughnut.templ
│ │ ├── chart_doughnut_legend.templ
│ │ ├── chart_doughnut_stacked.templ
│ │ ├── chart_line.templ
│ │ ├── chart_line_linear.templ
│ │ ├── chart_line_multiple.templ
│ │ ├── chart_line_step.templ
│ │ ├── chart_pie.templ
│ │ ├── chart_pie_legend.templ
│ │ ├── chart_pie_stacked.templ
│ │ ├── chart_radar.templ
│ │ ├── chart_radar_stacked.templ
│ │ ├── checkbox_card_default.templ
│ │ ├── checkbox_checked.templ
│ │ ├── checkbox_custom_icon.templ
│ │ ├── checkbox_default.templ
│ │ ├── checkbox_disabled.templ
│ │ ├── checkbox_form.templ
│ │ ├── checkbox_with_label.templ
│ │ ├── code_copy_button.templ
│ │ ├── code_custom_size.templ
│ │ ├── code_default.templ
│ │ ├── date_picker_custom_placeholder.templ
│ │ ├── date_picker_default.templ
│ │ ├── date_picker_disabled.templ
│ │ ├── date_picker_form.templ
│ │ ├── date_picker_formats.templ
│ │ ├── date_picker_selected_date.templ
│ │ ├── date_picker_with_label.templ
│ │ ├── drawer_default.templ
│ │ ├── drawer_positions.templ
│ │ ├── dropdown_default.templ
│ │ ├── embed.go
│ │ ├── icon_colored.templ
│ │ ├── icon_default.templ
│ │ ├── icon_filled.templ
│ │ ├── icon_sizes.templ
│ │ ├── input_default.templ
│ │ ├── input_disabled.templ
│ │ ├── input_file.templ
│ │ ├── input_form.templ
│ │ ├── input_otp_custom_length.templ
│ │ ├── input_otp_custom_styling.templ
│ │ ├── input_otp_default.templ
│ │ ├── input_otp_form.templ
│ │ ├── input_otp_password_type.templ
│ │ ├── input_otp_placeholder.templ
│ │ ├── input_otp_with_label.templ
│ │ ├── input_password.templ
│ │ ├── input_with_label.templ
│ │ ├── modal_default.templ
│ │ ├── pagination_default.templ
│ │ ├── pagination_with_helper.templ
│ │ ├── popover_default.templ
│ │ ├── popover_positions.templ
│ │ ├── popover_triggers.templ
│ │ ├── progress_colors.templ
│ │ ├── progress_default.templ
│ │ ├── progress_sizes.templ
│ │ ├── radio_card_default.templ
│ │ ├── radio_checked.templ
│ │ ├── radio_default.templ
│ │ ├── radio_disabled.templ
│ │ ├── radio_form.templ
│ │ ├── radio_with_label.templ
│ │ ├── rating_default.templ
│ │ ├── rating_form.templ
│ │ ├── rating_max_values.templ
│ │ ├── rating_precision.templ
│ │ ├── rating_styles.templ
│ │ ├── rating_with_label.templ
│ │ ├── select_box_default.templ
│ │ ├── select_box_disabled.templ
│ │ ├── select_box_form.templ
│ │ ├── select_box_with_label.templ
│ │ ├── separator_decorated.templ
│ │ ├── separator_default.templ
│ │ ├── separator_label.templ
│ │ ├── separator_vertical.templ
│ │ ├── skeleton_card.templ
│ │ ├── skeleton_dashboard.templ
│ │ ├── skeleton_default.templ
│ │ ├── skeleton_profile.templ
│ │ ├── slider_default.templ
│ │ ├── slider_disabled.templ
│ │ ├── slider_external_value.templ
│ │ ├── slider_steps.templ
│ │ ├── slider_value.templ
│ │ ├── spinner_colors.templ
│ │ ├── spinner_default.templ
│ │ ├── spinner_in_button.templ
│ │ ├── spinner_sizes.templ
│ │ ├── table.templ
│ │ ├── tabs_default.templ
│ │ ├── textarea_auto_resize.templ
│ │ ├── textarea_custom_rows.templ
│ │ ├── textarea_default.templ
│ │ ├── textarea_disabled.templ
│ │ ├── textarea_form.templ
│ │ ├── textarea_with_label.templ
│ │ ├── time_picker_12hour.templ
│ │ ├── time_picker_custom_placeholder.templ
│ │ ├── time_picker_default.templ
│ │ ├── time_picker_form.templ
│ │ ├── time_picker_label.templ
│ │ ├── time_picker_selected_time.templ
│ │ ├── toast_default.templ
│ │ ├── toast_playground.templ
│ │ ├── toggle_checked.templ
│ │ ├── toggle_default.templ
│ │ ├── toggle_disabled.templ
│ │ ├── toggle_form.templ
│ │ ├── toggle_with_label.templ
│ │ ├── tooltip_default.templ
│ │ └── tooltip_positions.templ
└── utils
│ ├── internal.go
│ └── templui.go
├── meta.json
├── middleware
└── csp.go
├── package-lock.json
├── package.json
├── shiki
├── Dockerfile
├── package-lock.json
├── package.json
└── server.js
└── static
├── robots.txt
├── sitemap.xml
└── static.go
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Ignore node_modules folder
2 | node_modules
3 |
4 | # Ignore logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 |
9 | # Ignore dotenv environment variable files
10 | .env
11 |
12 | # Ignore local development configuration files
13 | config/local.yml
14 |
15 | # Ignore temporary files and directories
16 | tmp/
17 | temp/
18 | *.tmp
19 |
20 | # Ignore build and cache directories
21 | build/
22 | dist/
23 | .cache/
24 |
25 | # Ignore OS generated files
26 | .DS_Store
27 | Thumbs.db
28 |
29 | # Ignore version control directories and files
30 | .git
31 | .gitignore
32 | .gitmodules
33 | .hg
34 | .hgignore
35 | .hgsubstate
36 | .svn
37 | .cvsignore
38 |
39 | # Ignore Python files
40 | *.pyc
41 | *.pyo
42 | __pycache__/
43 |
44 | # Ignore compiled source files
45 | *.o
46 | *.obj
47 | *.dll
48 | *.exe
49 | *.out
50 |
51 | # Ignore images and other binary files
52 | *.png
53 | *.jpg
54 | *.jpeg
55 | *.gif
56 | *.bmp
57 | *.tiff
58 | *.ico
59 |
60 | # Ignore compressed files
61 | *.zip
62 | *.tar.gz
63 | *.rar
64 | *.7z
65 | *.bz2
66 |
67 | # Ignore various IDE project files
68 | .vscode/
69 | .idea/
70 | *.suo
71 | *.user
72 | *.userossc
73 | *.sln.docstates
74 | .project
75 | .classpath
76 | .settings/
77 |
78 | # Ignore documentation files
79 | *.md
80 | *.pdf
81 |
82 | # Ignore build output files
83 | *.war
84 | *.jar
85 | *.ear
86 |
87 | # Ignore Kubernetes and Docker Compose files
88 | k8s/
89 | docker-compose.yml
90 | docker-compose.override.yml
91 |
92 | # Ignore other sensitive files
93 | *.pem
94 | *.key
95 | *.crt
96 | *.kdb
97 | *.jks
98 |
99 | # Ignore IntelliJ IDEA files
100 | *.iml
101 | *.iws
102 | .idea/
103 |
104 | # Ignore JetBrains Rider files
105 | *.sln.iml
106 | .idea/
107 |
108 | # Ignore WebStorm files
109 | .idea/
110 |
111 | # Ignore Visual Studio Code files
112 | .vscode/
113 |
114 | # Ignore SASS cache files
115 | .sass-cache/
116 |
117 | # Ignore Bower files
118 | .bower/
119 |
120 | # Ignore Gulp files
121 | .gulp/
122 |
123 | # Ignore Grunt files
124 | .grunt/
125 |
126 | # Ignore Yeoman files
127 | .yeoman/
128 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | GO_ENV=development
2 | SHIKI_URL=http://localhost:3000/highlight
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [axzilla]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Report a bug or unexpected behavior in templUI
4 | title: "[Bug] "
5 | labels: bug
6 | ---
7 |
8 | ## Describe the issue
9 |
10 | What did you expect to happen?
11 |
12 | ---
13 |
14 | ## Reproduction steps
15 |
16 | Steps to trigger the issue or paste minimal code.
17 |
18 | ---
19 |
20 | ## Environment
21 |
22 | - templUI version:
23 | - Go version:
24 | - Templ version:
25 | - Tailwind version:
26 | - Browser (if relevant):
27 |
28 | ---
29 |
30 | ## 🔍 Please include a minimal sample repo if possible
31 |
32 | A tiny repro repo or Gist helps a lot – thanks!
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest a new feature or improvement for templUI
4 | title: "[Feature] "
5 | labels: enhancement
6 | ---
7 |
8 | ## What would you like to see?
9 |
10 | Describe the feature or improvement you'd like to propose.
11 |
12 | ---
13 |
14 | ## Why is it useful?
15 |
16 | Explain how it helps you or improves templUI.
17 |
18 | ---
19 |
20 | ## Related components (if any)
21 |
22 | Mention any affected components, if applicable.
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Ask a question or get help using templUI
4 | title: "[Question] "
5 | labels: question
6 | ---
7 |
8 | ## What's your question?
9 |
10 | Be as specific as possible.
11 |
12 | ---
13 |
14 | ## What have you tried?
15 |
16 | If you tried something that didn’t work, mention it here.
17 |
18 | ---
19 |
20 | ## Optional: Link to code / example
21 |
22 | If relevant, link to a code snippet or repo.
23 |
--------------------------------------------------------------------------------
/.github/workflows/validate-html.yml:
--------------------------------------------------------------------------------
1 | name: Validate HTML
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | jobs:
10 | validate-html:
11 | name: 🧪 Build & Lint HTML from templUI Components
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - name: 🛎️ Checkout code
16 | uses: actions/checkout@v4
17 |
18 | - name: 🧰 Set up Go
19 | uses: actions/setup-go@v5
20 | with:
21 | go-version: "1.21"
22 |
23 | - name: 📦 Install Go + Node deps
24 | run: |
25 | sudo apt update
26 | sudo apt install -y curl
27 | curl -fsSL https://bun.sh/install | bash
28 | echo "$HOME/.bun/bin" >> $GITHUB_PATH
29 |
30 | - name: 📦 Install Templ
31 | run: go install github.com/a-h/templ/cmd/templ@v0.3.865
32 |
33 | - name: 📦 Install Node dependencies
34 | run: |
35 | npm ci
36 |
37 | - name: ✅ Run `make validate-html`
38 | run: make validate-html
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*_templ.go
2 | **/*_templ.txt
3 |
4 | .DS_Store
5 | bin
6 | node_modules
7 | tmp
8 | .env
9 |
10 | out
11 | assets/css/output.css
12 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug Go Docs",
6 | "type": "go",
7 | "request": "launch",
8 | "mode": "debug",
9 | "program": "${workspaceFolder}/cmd/docs",
10 | "args": ["serve", "--dir", "./pb_data"],
11 | "env": {},
12 | "cwd": "${workspaceFolder}"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build-Stage
2 | FROM golang:1.24 AS build
3 | WORKDIR /app
4 |
5 | # Copy the source code
6 | COPY . .
7 |
8 | # Install templ
9 | RUN go install github.com/a-h/templ/cmd/templ@latest
10 |
11 | # Generate templ files
12 | RUN templ generate
13 |
14 | # Install build dependencies
15 | RUN apt-get update && apt-get install -y curl wget && rm -rf /var/lib/apt/lists/*
16 |
17 | # Get the latest version from GitHub API and save it to version.txt
18 | RUN curl -s https://api.github.com/repos/axzilla/templui/releases/latest | grep tag_name | cut -d '"' -f 4 > version.txt || echo "unknown" > version.txt
19 |
20 | # Install Tailwind CSS standalone CLI
21 | RUN ARCH=$(uname -m) && \
22 | if [ "$ARCH" = "x86_64" ]; then \
23 | TAILWIND_URL="https://github.com/tailwindlabs/tailwindcss/releases/download/v4.1.3/tailwindcss-linux-x64"; \
24 | elif [ "$ARCH" = "aarch64" ]; then \
25 | TAILWIND_URL="https://github.com/tailwindlabs/tailwindcss/releases/download/v4.1.3/tailwindcss-linux-arm64"; \
26 | else \
27 | echo "Unsupported architecture: $ARCH"; exit 1; \
28 | fi && \
29 | wget -O tailwindcss "$TAILWIND_URL" && \
30 | chmod +x tailwindcss
31 |
32 | # Generate Tailwind CSS output
33 | RUN ./tailwindcss -i ./assets/css/input.css -o ./assets/css/output.css --minify
34 |
35 | # Build the application as a static binary
36 | RUN CGO_ENABLED=0 GOOS=linux go build -o main ./cmd/docs/main.go
37 |
38 | # Deploy-Stage
39 | FROM alpine:3.20.2
40 | WORKDIR /app
41 |
42 | # Install ca-certificates
43 | RUN apk add --no-cache ca-certificates
44 |
45 | # Set environment variable for runtime
46 | ENV GO_ENV=production
47 |
48 | # Copy the binary, version file, and CSS output
49 | COPY --from=build /app/main .
50 | COPY --from=build /app/version.txt .
51 | COPY --from=build /app/assets/css/output.css ./assets/css/output.css
52 |
53 | # Expose the port
54 | EXPOSE 8090
55 |
56 | # Command to run
57 | CMD ["./main"]
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Axel Adrian
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/assets/assets.go:
--------------------------------------------------------------------------------
1 | package assets
2 |
3 | import "embed"
4 |
5 | //go:embed css/* img/* js/*
6 | var Assets embed.FS
7 |
--------------------------------------------------------------------------------
/assets/img/aspect_ratio_placeholder.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axzilla/templui/85a39ea236948e44020e36ce40ab4aa4a611a7af/assets/img/aspect_ratio_placeholder.jpeg
--------------------------------------------------------------------------------
/assets/img/card_placeholder.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axzilla/templui/85a39ea236948e44020e36ce40ab4aa4a611a7af/assets/img/card_placeholder.jpeg
--------------------------------------------------------------------------------
/assets/img/demo/carousel-1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axzilla/templui/85a39ea236948e44020e36ce40ab4aa4a611a7af/assets/img/demo/carousel-1.jpeg
--------------------------------------------------------------------------------
/assets/img/demo/carousel-2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axzilla/templui/85a39ea236948e44020e36ce40ab4aa4a611a7af/assets/img/demo/carousel-2.jpeg
--------------------------------------------------------------------------------
/assets/img/demo/carousel-3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axzilla/templui/85a39ea236948e44020e36ce40ab4aa4a611a7af/assets/img/demo/carousel-3.jpeg
--------------------------------------------------------------------------------
/assets/img/readme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axzilla/templui/85a39ea236948e44020e36ce40ab4aa4a611a7af/assets/img/readme.png
--------------------------------------------------------------------------------
/assets/img/social-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axzilla/templui/85a39ea236948e44020e36ce40ab4aa4a611a7af/assets/img/social-preview.png
--------------------------------------------------------------------------------
/assets/img/tailwindcss-mark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 |
3 | services:
4 | go-app:
5 | build:
6 | context: .
7 | dockerfile: Dockerfile
8 | ports:
9 | # Maps host port 8090 to container port 8090 of the Go app
10 | - "${PORT:-8090}:8090"
11 | environment:
12 | # Override SHIKI_URL to use internal Docker service name
13 | SHIKI_URL: http://shiki-service:3000/highlight
14 | # Set port explicitly in case the app needs to read it from ENV
15 | PORT: "8090"
16 | # GIN_MODE: "release" # Example for other ENV variables
17 | depends_on:
18 | - shiki-service # Ensures shiki-service starts first
19 | restart: unless-stopped
20 | networks:
21 | - templui-net
22 | # Resource limits
23 | deploy:
24 | resources:
25 | limits:
26 | cpus: "1.0" # Limit Go app to 1 CPU core
27 | memory: 512M # Limit Go app to 512 MB RAM
28 | # volumes:
29 | # Optional: Local directory for persistent data if needed
30 | # - ./data:/app/data
31 |
32 | shiki-service:
33 | build:
34 | context: ./shiki
35 | dockerfile: Dockerfile
36 | # No 'ports' section here -> port 3000 is only internally accessible
37 | restart: unless-stopped
38 | networks:
39 | - templui-net
40 | # Resource limits
41 | deploy:
42 | resources:
43 | limits:
44 | cpus: "3.0" # Limit Shiki service (all instances) to 3 CPU cores total
45 | memory: 1536M # Limit Shiki service (all instances) to 1.5 GB RAM total
46 | # Optional: Environment variables for the Shiki server if needed
47 | # environment:
48 | # NODE_ENV: production
49 |
50 | networks:
51 | templui-net:
52 | driver: bridge
53 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | const htmlPlugin = require("@html-eslint/eslint-plugin");
2 | const eslintHTMLParser = require("@html-eslint/parser");
3 | const tailwind = require("eslint-plugin-html-tailwind");
4 |
5 | module.exports = [
6 | {
7 | files: ["out/**/*.html"],
8 | languageOptions: {
9 | parser: eslintHTMLParser,
10 | },
11 | plugins: {
12 | "@html-eslint": htmlPlugin,
13 | "html-tailwind": tailwind,
14 | },
15 | rules: {
16 | ...htmlPlugin.configs.recommended.rules,
17 | ...tailwind.configs.recommended.rules,
18 | "html-tailwind/classname-order": "off",
19 | "html-tailwind/no-style-attribute": "warn",
20 | "html-tailwind/no-contradicting-classnames": "warn",
21 | },
22 | },
23 | ];
24 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/axzilla/templui
2 |
3 | go 1.24.0
4 |
5 | require (
6 | github.com/Oudwins/tailwind-merge-go v0.2.0
7 | github.com/a-h/templ v0.3.898
8 | github.com/joho/godotenv v1.5.1
9 | )
10 |
11 | require (
12 | github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
13 | github.com/andybalholm/brotli v1.1.0 // indirect
14 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect
15 | github.com/cli/browser v1.3.0 // indirect
16 | github.com/fatih/color v1.16.0 // indirect
17 | github.com/fsnotify/fsnotify v1.7.0 // indirect
18 | github.com/mattn/go-colorable v0.1.13 // indirect
19 | github.com/mattn/go-isatty v0.0.20 // indirect
20 | github.com/natefinch/atomic v1.0.1 // indirect
21 | golang.org/x/mod v0.24.0 // indirect
22 | golang.org/x/net v0.39.0 // indirect
23 | golang.org/x/sync v0.13.0 // indirect
24 | golang.org/x/sys v0.32.0 // indirect
25 | golang.org/x/tools v0.32.0 // indirect
26 | )
27 |
28 | tool github.com/a-h/templ/cmd/templ
29 |
--------------------------------------------------------------------------------
/internal/components/aspectratio/aspectratio.templ:
--------------------------------------------------------------------------------
1 | package aspectratio
2 |
3 | import "github.com/axzilla/templui/internal/utils"
4 |
5 | type Ratio string
6 |
7 | const (
8 | RatioAuto Ratio = "auto"
9 | RatioSquare Ratio = "square"
10 | RatioVideo Ratio = "video"
11 | RatioPortrait Ratio = "portrait"
12 | RatioWide Ratio = "wide"
13 | )
14 |
15 | type Props struct {
16 | ID string
17 | Class string
18 | Attributes templ.Attributes
19 | Ratio Ratio
20 | }
21 |
22 | templ AspectRatio(props ...Props) {
23 | {{ var p Props }}
24 | if len(props) > 0 {
25 | {{ p = props[0] }}
26 | }
27 |
40 |
41 | { children... }
42 |
43 |
44 | }
45 |
46 | func ratioClass(ratio Ratio) string {
47 | switch ratio {
48 | case RatioSquare:
49 | return "aspect-square"
50 | case RatioVideo:
51 | return "aspect-video"
52 | case RatioPortrait:
53 | return "aspect-[3/4]"
54 | case RatioWide:
55 | return "aspect-[2/1]"
56 | case RatioAuto:
57 | return "aspect-auto"
58 | default:
59 | return "aspect-auto"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/internal/components/avatar/avatar.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | // IIFE
3 | function initAvatar(avatar) {
4 | const image = avatar.querySelector("[data-avatar-image]");
5 | const fallback = avatar.querySelector("[data-avatar-fallback]");
6 |
7 | if (image && fallback) {
8 | image.style.display = "none";
9 | fallback.style.display = "none";
10 |
11 | const showFallback = () => {
12 | image.style.display = "none";
13 | fallback.style.display = "";
14 | };
15 |
16 | const showImage = () => {
17 | image.style.display = "";
18 | fallback.style.display = "none";
19 | };
20 |
21 | if (image.complete) {
22 | image.naturalWidth > 0 && image.naturalHeight > 0
23 | ? showImage()
24 | : showFallback();
25 | } else {
26 | image.addEventListener("load", showImage, { once: true });
27 | image.addEventListener("error", showFallback, { once: true });
28 |
29 | setTimeout(() => {
30 | if (
31 | image.complete &&
32 | !(image.naturalWidth > 0 && image.naturalHeight > 0)
33 | ) {
34 | showFallback();
35 | }
36 | }, 50);
37 | }
38 | } else if (fallback) {
39 | fallback.style.display = "";
40 | } else if (image) {
41 | image.style.display = "";
42 | }
43 | }
44 |
45 | function initAllComponents(root = document) {
46 | if (root instanceof Element && root.matches("[data-avatar]")) {
47 | initAvatar(root);
48 | }
49 |
50 | for (const avatar of root.querySelectorAll("[data-avatar]")) {
51 | initAvatar(avatar);
52 | }
53 | }
54 |
55 | const handleHtmxSwap = (event) => {
56 | const target = event.detail.target || event.detail.elt;
57 | if (target instanceof Element) {
58 | requestAnimationFrame(() => initAllComponents(target));
59 | }
60 | };
61 |
62 | document.addEventListener("DOMContentLoaded", () => initAllComponents());
63 | document.body.addEventListener("htmx:afterSwap", handleHtmxSwap);
64 | document.body.addEventListener("htmx:oobAfterSwap", handleHtmxSwap);
65 | })(); // End of IIFE
66 |
--------------------------------------------------------------------------------
/internal/components/avatar/avatar.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function l(t){let e=t.querySelector("[data-avatar-image]"),a=t.querySelector("[data-avatar-fallback]");if(e&&a){e.style.display="none",a.style.display="none";let n=()=>{e.style.display="none",a.style.display=""},o=()=>{e.style.display="",a.style.display="none"};e.complete?e.naturalWidth>0&&e.naturalHeight>0?o():n():(e.addEventListener("load",o,{once:!0}),e.addEventListener("error",n,{once:!0}),setTimeout(()=>{e.complete&&!(e.naturalWidth>0&&e.naturalHeight>0)&&n()},50))}else a?a.style.display="":e&&(e.style.display="")}function i(t=document){t instanceof Element&&t.matches("[data-avatar]")&&l(t);for(let e of t.querySelectorAll("[data-avatar]"))l(e)}let s=t=>{let e=t.detail.target||t.detail.elt;e instanceof Element&&requestAnimationFrame(()=>i(e))};document.addEventListener("DOMContentLoaded",()=>i()),document.body.addEventListener("htmx:afterSwap",s),document.body.addEventListener("htmx:oobAfterSwap",s)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/badge/badge.templ:
--------------------------------------------------------------------------------
1 | package badge
2 |
3 | import "github.com/axzilla/templui/internal/utils"
4 |
5 | type Variant string
6 |
7 | const (
8 | VariantDefault Variant = "default"
9 | VariantSecondary Variant = "secondary"
10 | VariantDestructive Variant = "destructive"
11 | VariantOutline Variant = "outline"
12 | )
13 |
14 | type Props struct {
15 | ID string
16 | Class string
17 | Attributes templ.Attributes
18 | Variant Variant
19 | }
20 |
21 | templ Badge(props ...Props) {
22 | {{ var p Props }}
23 | if len(props) > 0 {
24 | {{ p = props[0] }}
25 | }
26 |
41 | { children... }
42 |
43 | }
44 |
45 | func (p Props) variantClasses() string {
46 | switch p.Variant {
47 | case VariantDestructive:
48 | return "border-transparent bg-destructive text-destructive-foreground"
49 | case VariantOutline:
50 | return "text-foreground border-border"
51 | case VariantSecondary:
52 | return "border-transparent bg-secondary text-secondary-foreground"
53 | default:
54 | return "border-transparent bg-primary text-primary-foreground"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/internal/components/checkbox/checkbox.templ:
--------------------------------------------------------------------------------
1 | package checkbox
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/icon"
5 | "github.com/axzilla/templui/internal/utils"
6 | )
7 |
8 | type Props struct {
9 | ID string
10 | Class string
11 | Attributes templ.Attributes
12 | Name string
13 | Value string
14 | Disabled bool
15 | Required bool
16 | Checked bool
17 | Icon templ.Component
18 | }
19 |
20 | templ Checkbox(props ...Props) {
21 | {{ var p Props }}
22 | if len(props) > 0 {
23 | {{ p = props[0] }}
24 | }
25 |
26 |
53 |
62 | if p.Icon != nil {
63 | @p.Icon
64 | } else {
65 | @icon.Check(icon.Props{Size: 12})
66 | }
67 |
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/internal/components/code/code.templ:
--------------------------------------------------------------------------------
1 | package code
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/icon"
5 | "github.com/axzilla/templui/internal/utils"
6 | )
7 |
8 | type Size string
9 |
10 | const (
11 | SizeSm Size = "sm"
12 | SizeLg Size = "lg"
13 | SizeFull Size = "full"
14 | )
15 |
16 | type Props struct {
17 | ID string
18 | Class string
19 | Attrs templ.Attributes
20 | Language string
21 | ShowCopyButton bool
22 | Size Size
23 | CodeClass string
24 | }
25 |
26 | templ Code(props ...Props) {
27 |
28 | {{ var p Props }}
29 | if len(props) > 0 {
30 | {{ p = props[0] }}
31 | }
32 | if p.ID == "" {
33 | {{ p.ID = "code-" + utils.RandomID() }}
34 | }
35 |
41 |
42 |
56 | { children... }
57 |
58 |
59 | if p.ShowCopyButton {
60 |
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/internal/components/dropdown/dropdown.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | // IIFE
3 | function handleDropdownItemClick(event) {
4 | const item = event.currentTarget;
5 | const popoverContent = item.closest("[data-popover-id]");
6 | if (popoverContent) {
7 | const popoverId = popoverContent.dataset.popoverId;
8 | if (window.closePopover) {
9 | window.closePopover(popoverId, true);
10 | } else {
11 | console.warn("popover.Script's closePopover function not found.");
12 | document.body.click(); // Fallback
13 | }
14 | }
15 | }
16 |
17 | function initAllComponents(root = document) {
18 | // Select items with 'data-dropdown-item' but not 'data-dropdown-submenu-trigger'
19 | const items = root.querySelectorAll(
20 | "[data-dropdown-item]:not([data-dropdown-submenu-trigger])"
21 | );
22 | items.forEach((item) => {
23 | item.removeEventListener("click", handleDropdownItemClick);
24 | item.addEventListener("click", handleDropdownItemClick);
25 | });
26 | }
27 |
28 | const handleHtmxSwap = (event) => {
29 | const target = event.detail.target || event.detail.elt;
30 | if (target instanceof Element) {
31 | requestAnimationFrame(() => initAllComponents(target));
32 | }
33 | };
34 |
35 | document.addEventListener("DOMContentLoaded", () => initAllComponents());
36 | document.body.addEventListener("htmx:afterSwap", handleHtmxSwap);
37 | document.body.addEventListener("htmx:oobAfterSwap", handleHtmxSwap);
38 | })(); // End of IIFE
39 |
--------------------------------------------------------------------------------
/internal/components/dropdown/dropdown.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function n(e){let t=e.currentTarget.closest("[data-popover-id]");if(t){let c=t.dataset.popoverId;window.closePopover?window.closePopover(c,!0):(console.warn("popover.Script's closePopover function not found."),document.body.click())}}function d(e=document){e.querySelectorAll("[data-dropdown-item]:not([data-dropdown-submenu-trigger])").forEach(t=>{t.removeEventListener("click",n),t.addEventListener("click",n)})}let r=e=>{let o=e.detail.target||e.detail.elt;o instanceof Element&&requestAnimationFrame(()=>d(o))};document.addEventListener("DOMContentLoaded",()=>d()),document.body.addEventListener("htmx:afterSwap",r),document.body.addEventListener("htmx:oobAfterSwap",r)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/embed.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import "embed"
4 |
5 | //go:embed **/*.templ **/*.go
6 | var TemplFiles embed.FS
7 |
--------------------------------------------------------------------------------
/internal/components/input/input.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | function initPasswordToggle(button) {
3 | if (button.hasAttribute("data-password-initialized")) {
4 | return;
5 | }
6 |
7 | button.setAttribute("data-password-initialized", "true");
8 |
9 | button.addEventListener("click", function (event) {
10 | const inputId = button.getAttribute("data-toggle-password");
11 | const input = document.getElementById(inputId);
12 | if (input) {
13 | const iconOpen = button.querySelector(".icon-open");
14 | const iconClosed = button.querySelector(".icon-closed");
15 |
16 | if (input.type === "password") {
17 | input.type = "text";
18 | iconOpen.classList.add("hidden");
19 | iconClosed.classList.remove("hidden");
20 | } else {
21 | input.type = "password";
22 | iconOpen.classList.remove("hidden");
23 | iconClosed.classList.add("hidden");
24 | }
25 | }
26 | });
27 | }
28 |
29 | function initAllComponents(root = document) {
30 | const buttons = root.querySelectorAll(
31 | "[data-toggle-password]:not([data-password-initialized])"
32 | );
33 | buttons.forEach((button) => {
34 | initPasswordToggle(button);
35 | });
36 | }
37 |
38 | const handleHtmxSwap = (event) => {
39 | const target = event.detail.target || event.detail.elt;
40 | if (target instanceof Element) {
41 | requestAnimationFrame(() => initAllComponents(target));
42 | }
43 | };
44 |
45 | document.addEventListener("DOMContentLoaded", () => initAllComponents());
46 | document.body.addEventListener("htmx:afterSwap", handleHtmxSwap);
47 | document.body.addEventListener("htmx:oobAfterSwap", handleHtmxSwap);
48 | })();
49 |
--------------------------------------------------------------------------------
/internal/components/input/input.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function r(t){t.hasAttribute("data-password-initialized")||(t.setAttribute("data-password-initialized","true"),t.addEventListener("click",function(e){let d=t.getAttribute("data-toggle-password"),n=document.getElementById(d);if(n){let o=t.querySelector(".icon-open"),a=t.querySelector(".icon-closed");n.type==="password"?(n.type="text",o.classList.add("hidden"),a.classList.remove("hidden")):(n.type="password",o.classList.remove("hidden"),a.classList.add("hidden"))}}))}function i(t=document){t.querySelectorAll("[data-toggle-password]:not([data-password-initialized])").forEach(d=>{r(d)})}let s=t=>{let e=t.detail.target||t.detail.elt;e instanceof Element&&requestAnimationFrame(()=>i(e))};document.addEventListener("DOMContentLoaded",()=>i()),document.body.addEventListener("htmx:afterSwap",s),document.body.addEventListener("htmx:oobAfterSwap",s)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/label/label.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function a(t){if(!t.hasAttribute("for")||!t.hasAttribute("data-disabled-style"))return;let e=t.getAttribute("for"),n=e?document.getElementById(e):null,d=t.getAttribute("data-disabled-style");if(!d)return;let o=d.split(" ").filter(Boolean);function r(){n&&n.disabled?t.classList.add(...o):t.classList.remove(...o)}n&&new MutationObserver(u=>{for(let l of u)l.type==="attributes"&&l.attributeName==="disabled"&&r()}).observe(n,{attributes:!0,attributeFilter:["disabled"]}),r()}function s(t=document){t instanceof Element&&t.matches("label[for][data-disabled-style]")&&a(t);for(let e of t.querySelectorAll("label[for][data-disabled-style]"))a(e)}let i=t=>{let e=t.detail.target||t.detail.elt;e instanceof Element&&requestAnimationFrame(()=>s(e))};document.addEventListener("DOMContentLoaded",()=>s()),document.body.addEventListener("htmx:afterSwap",i),document.body.addEventListener("htmx:oobAfterSwap",i)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/label/label.templ:
--------------------------------------------------------------------------------
1 | package label
2 |
3 | import "github.com/axzilla/templui/internal/utils"
4 |
5 | type Props struct {
6 | ID string
7 | Class string
8 | Attributes templ.Attributes
9 | For string
10 | Error string
11 | }
12 |
13 | templ Label(props ...Props) {
14 | {{ var p Props }}
15 | if len(props) > 0 {
16 | {{ p = props[0] }}
17 | }
18 |
37 | }
38 |
--------------------------------------------------------------------------------
/internal/components/main.js:
--------------------------------------------------------------------------------
1 | import "./avatar/avatar.js";
2 | import "./calendar/calendar.js";
3 | import "./carousel/carousel.js";
4 | import "./chart/chart.js";
5 | import "./code/code.js";
6 | import "./datepicker/datepicker.js";
7 | import "./drawer/drawer.js";
8 | import "./dropdown/dropdown.js";
9 | import "./input/input.js";
10 | import "./inputotp/inputotp.js";
11 | import "./label/label.js";
12 | import "./modal/modal.js";
13 | import "./popover/popover.js";
14 | import "./progress/progress.js";
15 | import "./rating/rating.js";
16 | import "./selectbox/selectbox.js";
17 | import "./slider/slider.js";
18 | import "./tabs/tabs.js";
19 | import "./textarea/textarea.js";
20 | import "./toast/toast.js";
21 |
--------------------------------------------------------------------------------
/internal/components/progress/progress.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | function updateProgressWidth(progressBar) {
3 | if (!progressBar) return;
4 |
5 | const indicator = progressBar.querySelector("[data-progress-indicator]");
6 | if (!indicator) return;
7 |
8 | const value = parseFloat(progressBar.getAttribute("aria-valuenow") || "0");
9 | let max = parseFloat(progressBar.getAttribute("aria-valuemax") || "100");
10 | if (max <= 0) max = 100;
11 |
12 | let percentage = 0;
13 | if (max > 0) {
14 | percentage = (Math.max(0, Math.min(value, max)) / max) * 100;
15 | }
16 |
17 | indicator.style.width = percentage + "%";
18 | }
19 |
20 | function initAllComponents(root = document) {
21 | if (root instanceof Element && root.matches('[role="progressbar"]')) {
22 | updateProgressWidth(root);
23 | }
24 | if (root && typeof root.querySelectorAll === "function") {
25 | for (const progressBar of root.querySelectorAll('[role="progressbar"]')) {
26 | updateProgressWidth(progressBar);
27 | }
28 | }
29 | }
30 |
31 | const handleHtmxSwap = (event) => {
32 | const target = event.detail.target || event.detail.elt;
33 | if (target instanceof Element) {
34 | requestAnimationFrame(() => initAllComponents(target));
35 | }
36 | };
37 |
38 | document.addEventListener("DOMContentLoaded", () => initAllComponents());
39 | document.body.addEventListener("htmx:afterSwap", handleHtmxSwap);
40 | document.body.addEventListener("htmx:oobAfterSwap", handleHtmxSwap);
41 | })();
42 |
--------------------------------------------------------------------------------
/internal/components/progress/progress.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function a(e){if(!e)return;let t=e.querySelector("[data-progress-indicator]");if(!t)return;let l=parseFloat(e.getAttribute("aria-valuenow")||"0"),n=parseFloat(e.getAttribute("aria-valuemax")||"100");n<=0&&(n=100);let o=0;n>0&&(o=Math.max(0,Math.min(l,n))/n*100),t.style.width=o+"%"}function i(e=document){if(e instanceof Element&&e.matches('[role="progressbar"]')&&a(e),e&&typeof e.querySelectorAll=="function")for(let t of e.querySelectorAll('[role="progressbar"]'))a(t)}let r=e=>{let t=e.detail.target||e.detail.elt;t instanceof Element&&requestAnimationFrame(()=>i(t))};document.addEventListener("DOMContentLoaded",()=>i()),document.body.addEventListener("htmx:afterSwap",r),document.body.addEventListener("htmx:oobAfterSwap",r)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/radio/radio.templ:
--------------------------------------------------------------------------------
1 | package radio
2 |
3 | import "github.com/axzilla/templui/internal/utils"
4 |
5 | type Props struct {
6 | ID string
7 | Class string
8 | Attributes templ.Attributes
9 | Name string
10 | Value string
11 | Disabled bool
12 | Required bool
13 | Checked bool
14 | }
15 |
16 | templ Radio(props ...Props) {
17 | {{ var p Props }}
18 | if len(props) > 0 {
19 | {{ p = props[0] }}
20 | }
21 |
53 | }
54 |
--------------------------------------------------------------------------------
/internal/components/skeleton/skeleton.templ:
--------------------------------------------------------------------------------
1 | package skeleton
2 |
3 | import "github.com/axzilla/templui/internal/utils"
4 |
5 | type Props struct {
6 | ID string
7 | Class string
8 | Attributes templ.Attributes
9 | }
10 |
11 | templ Skeleton(props ...Props) {
12 | {{ var p Props }}
13 | if len(props) > 0 {
14 | {{ p = props[0] }}
15 | }
16 |
28 | }
29 |
--------------------------------------------------------------------------------
/internal/components/slider/slider.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | // IIFE
3 | function initSlider(sliderInput) {
4 | if (sliderInput.hasAttribute("data-initialized")) return;
5 |
6 | sliderInput.setAttribute("data-initialized", "true");
7 |
8 | const sliderId = sliderInput.id;
9 | if (!sliderId) return;
10 |
11 | const valueElements = document.querySelectorAll(
12 | `[data-slider-value][data-slider-value-for="${sliderId}"]`
13 | );
14 |
15 | function updateValues() {
16 | valueElements.forEach((el) => {
17 | el.textContent = sliderInput.value;
18 | });
19 | }
20 |
21 | updateValues();
22 | sliderInput.addEventListener("input", updateValues);
23 | }
24 |
25 | function initAllComponents(root = document) {
26 | if (
27 | root instanceof Element &&
28 | root.matches('input[type="range"][data-slider-input]')
29 | ) {
30 | initSlider(root);
31 | }
32 | for (const slider of root.querySelectorAll(
33 | 'input[type="range"][data-slider-input]:not([data-initialized])'
34 | )) {
35 | initSlider(slider);
36 | }
37 | }
38 |
39 | const handleHtmxSwap = (event) => {
40 | const target = event.detail.target || event.detail.elt;
41 | if (target instanceof Element) {
42 | requestAnimationFrame(() => initAllComponents(target));
43 | }
44 | };
45 |
46 | document.addEventListener("DOMContentLoaded", () => initAllComponents());
47 | document.body.addEventListener("htmx:afterSwap", handleHtmxSwap);
48 | document.body.addEventListener("htmx:oobAfterSwap", handleHtmxSwap);
49 | })(); // End of IIFE
50 |
--------------------------------------------------------------------------------
/internal/components/slider/slider.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function n(t){if(t.hasAttribute("data-initialized"))return;t.setAttribute("data-initialized","true");let e=t.id;if(!e)return;let o=document.querySelectorAll(`[data-slider-value][data-slider-value-for="${e}"]`);function d(){o.forEach(l=>{l.textContent=t.value})}d(),t.addEventListener("input",d)}function a(t=document){t instanceof Element&&t.matches('input[type="range"][data-slider-input]')&&n(t);for(let e of t.querySelectorAll('input[type="range"][data-slider-input]:not([data-initialized])'))n(e)}let i=t=>{let e=t.detail.target||t.detail.elt;e instanceof Element&&requestAnimationFrame(()=>a(e))};document.addEventListener("DOMContentLoaded",()=>a()),document.body.addEventListener("htmx:afterSwap",i),document.body.addEventListener("htmx:oobAfterSwap",i)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/spinner/spinner.templ:
--------------------------------------------------------------------------------
1 | package spinner
2 |
3 | import "github.com/axzilla/templui/internal/utils"
4 |
5 | type Size string
6 |
7 | const (
8 | SizeSm Size = "sm"
9 | SizeMd Size = "md"
10 | SizeLg Size = "lg"
11 | )
12 |
13 | type Props struct {
14 | ID string
15 | Class string
16 | Attributes templ.Attributes
17 | Size Size
18 | Color string
19 | }
20 |
21 | templ Spinner(props ...Props) {
22 | {{ var p Props }}
23 | if len(props) > 0 {
24 | {{ p = props[0] }}
25 | }
26 |
60 | }
61 |
62 | func sizeClass(size Size) string {
63 | switch size {
64 | case SizeSm:
65 | return "w-6 h-6"
66 | case SizeLg:
67 | return "w-12 h-12"
68 | default:
69 | return "w-8 h-8"
70 | }
71 | }
72 |
73 | func borderSizeClass(size Size) string {
74 | switch size {
75 | case SizeSm:
76 | return "border-[3px]"
77 | case SizeLg:
78 | return "border-[5px]"
79 | default:
80 | return "border-4"
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/internal/components/tabs/tabs.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function o(t){if(t.hasAttribute("data-initialized"))return;t.setAttribute("data-initialized","true");let s=t.dataset.tabsId;if(!s)return;let n=Array.from(t.querySelectorAll(`[data-tabs-trigger][data-tabs-id="${s}"]`)),b=Array.from(t.querySelectorAll(`[data-tabs-content][data-tabs-id="${s}"]`)),d=t.querySelector(`[data-tabs-marker][data-tabs-id="${s}"]`);function g(e){!d||!e||(d.style.width=e.offsetWidth+"px",d.style.height=e.offsetHeight+"px",d.style.left=e.offsetLeft+"px")}function c(e){let u=null;for(let a of n){let i=a.dataset.tabsValue===e;a.dataset.state=i?"active":"inactive",a.classList.toggle("text-foreground",i),a.classList.toggle("bg-background",i),a.classList.toggle("shadow-xs",i),i&&(u=a)}for(let a of b){let i=a.dataset.tabsValue===e;a.dataset.state=i?"active":"inactive",a.classList.toggle("hidden",!i)}g(u)}let f=n.find(e=>e.dataset.state==="active")||n[0];f&&c(f.dataset.tabsValue);for(let e of n)e.addEventListener("click",()=>{c(e.dataset.tabsValue)})}function r(t=document){t instanceof Element&&t.matches("[data-tabs]")&&o(t);for(let s of t.querySelectorAll("[data-tabs]:not([data-initialized])"))o(s)}let l=t=>{let s=t.detail.target||t.detail.elt;s instanceof Element&&requestAnimationFrame(()=>r(s))};document.addEventListener("DOMContentLoaded",()=>r()),document.body.addEventListener("htmx:afterSwap",l),document.body.addEventListener("htmx:oobAfterSwap",l)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/textarea/textarea.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | // IIFE
3 | function initTextarea(textarea) {
4 | if (textarea.hasAttribute("data-initialized")) return;
5 |
6 | textarea.setAttribute("data-initialized", "true");
7 |
8 | const autoResize = textarea.dataset.autoResize === "true";
9 | if (!autoResize) return;
10 |
11 | const computedStyle = window.getComputedStyle(textarea);
12 | const initialMinHeight = computedStyle.minHeight;
13 |
14 | function resize() {
15 | textarea.style.height = initialMinHeight;
16 | textarea.style.height = `${textarea.scrollHeight}px`;
17 | }
18 |
19 | resize();
20 | textarea.addEventListener("input", resize);
21 | }
22 |
23 | function initAllComponents(root = document) {
24 | if (root instanceof Element && root.matches("textarea[data-textarea]")) {
25 | initTextarea(root);
26 | }
27 | for (const textarea of root.querySelectorAll(
28 | "textarea[data-textarea]:not([data-initialized])"
29 | )) {
30 | initTextarea(textarea);
31 | }
32 | }
33 |
34 | const handleHtmxSwap = (event) => {
35 | const target = event.detail.target || event.detail.elt;
36 | if (target instanceof Element) {
37 | requestAnimationFrame(() => initAllComponents(target));
38 | }
39 | };
40 |
41 | document.addEventListener("DOMContentLoaded", () => initAllComponents());
42 | document.body.addEventListener("htmx:afterSwap", handleHtmxSwap);
43 | document.body.addEventListener("htmx:oobAfterSwap", handleHtmxSwap);
44 | })(); // End of IIFE
45 |
--------------------------------------------------------------------------------
/internal/components/textarea/textarea.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){function i(t){if(t.hasAttribute("data-initialized")||(t.setAttribute("data-initialized","true"),!(t.dataset.autoResize==="true")))return;let o=window.getComputedStyle(t).minHeight;function d(){t.style.height=o,t.style.height=`${t.scrollHeight}px`}d(),t.addEventListener("input",d)}function n(t=document){t instanceof Element&&t.matches("textarea[data-textarea]")&&i(t);for(let e of t.querySelectorAll("textarea[data-textarea]:not([data-initialized])"))i(e)}let a=t=>{let e=t.detail.target||t.detail.elt;e instanceof Element&&requestAnimationFrame(()=>n(e))};document.addEventListener("DOMContentLoaded",()=>n()),document.body.addEventListener("htmx:afterSwap",a),document.body.addEventListener("htmx:oobAfterSwap",a)})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/textarea/textarea.templ:
--------------------------------------------------------------------------------
1 | package textarea
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/utils"
5 | "strconv"
6 | )
7 |
8 | type Props struct {
9 | ID string
10 | Class string
11 | Attributes templ.Attributes
12 | Name string
13 | Value string
14 | Placeholder string
15 | Rows int
16 | AutoResize bool
17 | Disabled bool
18 | Required bool
19 | }
20 |
21 | templ Textarea(props ...Props) {
22 | {{ var p Props }}
23 | if len(props) > 0 {
24 | {{ p = props[0] }}
25 | }
26 | if p.ID == "" {
27 | {{ p.ID = utils.RandomID() }}
28 | }
29 |
62 | }
63 |
--------------------------------------------------------------------------------
/internal/components/toast/toast.min.js:
--------------------------------------------------------------------------------
1 | (()=>{(function(){if(typeof window.toastHandler>"u"){let a=function(t){if(window.toasts.has(t))return;let s=parseInt(t.dataset.duration||"0"),i=t.querySelector("[data-toast-progress]"),d=t.querySelector("[data-toast-dismiss]"),e={timer:null,remaining:s,startTime:Date.now(),progress:i,paused:!1};window.toasts.set(t,e);function u(){clearTimeout(e.timer),t.classList.remove("toast-enter-active"),t.classList.add("toast-leave-active"),t.addEventListener("transitionend",()=>{t.remove(),window.toasts.delete(t)},{once:!0})}function c(n){n<=0||(clearTimeout(e.timer),e.startTime=Date.now(),e.remaining=n,e.paused=!1,e.timer=setTimeout(u,n),e.progress&&(e.progress.style.transition=`width ${n}ms linear`,e.progress.offsetWidth,e.progress.style.width="0%"))}function m(){if(!(e.paused||e.remaining<=0)&&(clearTimeout(e.timer),e.remaining-=Date.now()-e.startTime,e.paused=!0,e.progress)){let n=window.getComputedStyle(e.progress).width;e.progress.style.transition="none",e.progress.style.width=n}}function l(){!e.paused||e.remaining<=0||c(e.remaining)}s>0&&(t.addEventListener("mouseenter",m),t.addEventListener("mouseleave",l)),d&&d.addEventListener("click",u),setTimeout(()=>{t.classList.add("toast-enter-active"),e.progress&&(e.progress.style.width="100%"),c(s)},50)},r=function(t=document){let s=[];t instanceof Element&&t.matches("[data-toast]")&&(window.toasts.has(t)||s.push(t)),t&&typeof t.querySelectorAll=="function"&&t.querySelectorAll("[data-toast]").forEach(i=>{window.toasts.has(i)||s.push(i)}),s.forEach(a)};var f=a,w=r;window.toastHandler=!0,window.toasts=new Map;let o=t=>{let s=t.detail.target||t.detail.elt;s instanceof Element&&requestAnimationFrame(()=>r(s))};document.addEventListener("DOMContentLoaded",()=>r()),document.body.addEventListener("htmx:afterSwap",o),document.body.addEventListener("htmx:oobAfterSwap",o)}})();})();
2 |
--------------------------------------------------------------------------------
/internal/components/toggle/toggle.templ:
--------------------------------------------------------------------------------
1 | package toggle
2 |
3 | import "github.com/axzilla/templui/internal/utils"
4 |
5 | type Props struct {
6 | ID string
7 | Class string
8 | Attributes templ.Attributes
9 | Name string
10 | Disabled bool
11 | Checked bool
12 | }
13 |
14 | templ Toggle(props ...Props) {
15 | {{ var p Props }}
16 | if len(props) > 0 {
17 | {{ p = props[0] }}
18 | }
19 | if p.ID == "" {
20 | {{ p.ID = utils.RandomID() }}
21 | }
22 |
62 | }
63 |
--------------------------------------------------------------------------------
/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 |
8 | "github.com/joho/godotenv"
9 | )
10 |
11 | type Config struct {
12 | GoEnv string
13 | }
14 |
15 | var AppConfig *Config
16 |
17 | func LoadConfig() {
18 | err := godotenv.Load()
19 | if err != nil {
20 | fmt.Println("Error loading .env file")
21 | } else {
22 | log.Println(".env file initialized.")
23 | }
24 |
25 | AppConfig = &Config{
26 | GoEnv: os.Getenv("GO_ENV"),
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internal/ctxkeys/ctxkeys.go:
--------------------------------------------------------------------------------
1 | package ctxkeys
2 |
3 | type contextKey string
4 |
5 | const (
6 | URLPathValue = contextKey("url_path_value")
7 | Version = contextKey("version")
8 | )
9 |
--------------------------------------------------------------------------------
/internal/ui/icons/github.templ:
--------------------------------------------------------------------------------
1 | package wrappedicon
2 |
3 | import "strconv"
4 |
5 | templ GitHub(size int) {
6 |
17 | }
18 |
--------------------------------------------------------------------------------
/internal/ui/icons/js.templ:
--------------------------------------------------------------------------------
1 | package wrappedicon
2 |
3 | import "strconv"
4 |
5 | templ JS(size int) {
6 |
33 | }
34 |
--------------------------------------------------------------------------------
/internal/ui/icons/tailwind.templ:
--------------------------------------------------------------------------------
1 | package wrappedicon
2 |
3 | import "strconv"
4 |
5 | templ Tailwind(size int) {
6 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/icons/x.templ:
--------------------------------------------------------------------------------
1 | package wrappedicon
2 |
3 | import "strconv"
4 |
5 | templ X(size int) {
6 |
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/layouts/docs.templ:
--------------------------------------------------------------------------------
1 | package layouts
2 |
3 | import "github.com/axzilla/templui/internal/ui/modules"
4 |
5 | templ DocsLayout(title, description string, tocItems []modules.TableOfContentsItem) {
6 | @BaseLayout(title, description) {
7 | @modules.TableOfContentsScript()
8 |
9 | @modules.AnnouncementBar()
10 | @modules.Navbar()
11 |
12 |
13 |
14 | @modules.Sidebar()
15 |
16 |
17 |
18 |
19 |
20 | { children... }
21 |
22 |
23 | @modules.Footer()
24 |
25 |
26 |
27 | @modules.TableOfContents(modules.TableOfContentsProps{
28 | Items: tocItems,
29 | })
30 |
31 |
32 |
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/internal/ui/modules/announcement_bar.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | templ AnnouncementBar() {
4 |
5 |
6 |
10 |
11 |
14 | templUI Pro coming soon
15 |
16 |
17 |
18 |
19 | 50% OFF
20 | Join Waitlist
21 |
25 |
26 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/internal/ui/modules/codesnippet_embed.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import "embed"
4 |
5 | templ CodeSnippetFromEmbedded(filename, language string, embed embed.FS) {
6 | if content, err := embed.ReadFile(filename); err != nil {
7 | Error reading file: { filename }: { err.Error() }
8 | } else {
9 | @Code(CodeProps{
10 | Language: "templ",
11 | ShowCopyButton: true,
12 | CodeContent: string(content),
13 | })
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/modules/component_usage.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | type ComponentUsageProps struct {
4 | ComponentName string
5 | // HasJS bool
6 | JSFiles []string
7 | }
8 |
9 | templ ComponentUsage(props ComponentUsageProps) {
10 | if len(props.JSFiles) == 0 {
11 | @Code(CodeProps{
12 | CodeContent: "templui add " + props.ComponentName,
13 | ShowCopyButton: true,
14 | })
15 | } else {
16 |
17 | -
18 |
Install the component
19 | @Code(CodeProps{
20 | CodeContent: "templui add " + props.ComponentName,
21 | ShowCopyButton: true,
22 | })
23 |
24 | -
25 |
Add the JavaScript to your layout
26 | {{
27 | var files string
28 | for _, jsFile := range props.JSFiles {
29 | files += "@" + jsFile + ".Script()\n"
30 | }
31 | }}
32 | @Code(CodeProps{
33 | CodeContent: files,
34 | ShowCopyButton: true,
35 | })
36 | Call this template in your base layout file (e.g., in the <head> section).
37 |
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/internal/ui/modules/container_wrapper.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | type ContainerWrapperProps struct {
4 | Title string
5 | Description string
6 | ID string
7 | }
8 |
9 | templ ContainerWrapper(p ContainerWrapperProps) {
10 |
11 |
12 |
{ p.Title }
13 | if p.Description != "" {
14 |
{ p.Description }
15 | }
16 |
17 |
18 | { children... }
19 |
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/internal/ui/modules/example_wrapper.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/tabs"
5 | "github.com/axzilla/templui/internal/ui/showcase"
6 | )
7 |
8 | type showcaseWrapperProps struct {
9 | Content templ.Component
10 | }
11 |
12 | templ showcaseWrapper(p showcaseWrapperProps) {
13 |
14 | @p.Content
15 |
16 | }
17 |
18 | type ExampleWrapperProps struct {
19 | SectionName string
20 | ShowcaseFile templ.Component
21 | PreviewCodeFile string
22 | ID string // For #id in URL Link
23 | }
24 |
25 | templ ExampleWrapper(p ExampleWrapperProps) {
26 |
31 |
{ p.SectionName }
32 | @tabs.Tabs(tabs.Props{
33 | ID: "example-" + p.ID,
34 | }) {
35 | @tabs.List(tabs.ListProps{
36 | Class: "md:w-1/2",
37 | }) {
38 | @tabs.Trigger(tabs.TriggerProps{
39 | Value: "preview",
40 | IsActive: true,
41 | }) {
42 | Preview
43 | }
44 | @tabs.Trigger(tabs.TriggerProps{
45 | Value: "code",
46 | }) {
47 | Code
48 | }
49 | }
50 |
51 | @tabs.Content(tabs.ContentProps{
52 | Value: "preview",
53 | IsActive: true,
54 | }) {
55 | @showcaseWrapper(showcaseWrapperProps{
56 | Content: p.ShowcaseFile,
57 | })
58 | }
59 | @tabs.Content(tabs.ContentProps{
60 | Value: "code",
61 | }) {
62 | @CodeSnippetFromEmbedded(p.PreviewCodeFile, "go", showcase.TemplFiles)
63 | }
64 |
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/internal/ui/modules/footer.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "fmt"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | "time"
7 | )
8 |
9 | templ Footer() {
10 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/modules/seo.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | // SEO is a simple module for all important SEO tags
4 | templ SEO(title, description, path string) {
5 | {{ title = title + " | templUI" }}
6 | { title }
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/internal/ui/modules/sidebar.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import "github.com/axzilla/templui/internal/shared"
4 | import "github.com/axzilla/templui/internal/ctxkeys"
5 |
6 | templ Sidebar() {
7 |
35 | }
36 |
--------------------------------------------------------------------------------
/internal/ui/modules/themeswitcher.templ:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ themeSwitcherHandler() {
9 | {{ handle := templ.NewOnceHandle() }}
10 | @handle.Once() {
11 |
23 | }
24 | }
25 |
26 | type ThemeSwitcherProps struct {
27 | Class string
28 | }
29 |
30 | templ ThemeSwitcher(props ...ThemeSwitcherProps) {
31 | {{ var p ThemeSwitcherProps }}
32 | if len(props) > 0 {
33 | {{ p = props[0] }}
34 | }
35 | @themeSwitcherHandler()
36 | @button.Button(button.Props{
37 | Size: button.SizeIcon,
38 | Variant: button.VariantGhost,
39 | Class: p.Class,
40 | Attributes: templ.Attributes{
41 | "@click": "toggleTheme",
42 | },
43 | }) {
44 | @icon.SunMedium()
45 | }
46 | }
47 |
48 | templ DynamicThemeIcon() {
49 |
50 |
51 | @LightIcon()
52 |
53 |
54 | @DarkIcon()
55 |
56 |
57 | }
58 |
59 | templ DarkIcon() {
60 | @icon.Moon()
61 | }
62 |
63 | templ LightIcon() {
64 | @icon.SunMedium()
65 | }
66 |
--------------------------------------------------------------------------------
/internal/ui/pages/accordion.templ:
--------------------------------------------------------------------------------
1 |
2 | package pages
3 |
4 | import (
5 | "github.com/axzilla/templui/internal/ui/layouts"
6 | "github.com/axzilla/templui/internal/ui/modules"
7 | "github.com/axzilla/templui/internal/ui/showcase"
8 | )
9 |
10 | templ Accordion() {
11 | @layouts.DocsLayout(
12 | "Accordion",
13 | "Vertically stacked interactive sections to organize content.",
14 | []modules.TableOfContentsItem{
15 | {
16 | ID: "installation",
17 | Text: "Installation",
18 | },
19 | },
20 | ) {
21 | @modules.PageWrapper(modules.PageWrapperProps{
22 | Name: "Accordion",
23 | Description: templ.Raw("Vertically stacked interactive sections to organize content."),
24 | Tailwind: true,
25 | Breadcrumbs: modules.Breadcrumbs{
26 | Items: []modules.BreadcrumbItem{
27 | {
28 | Text: "Docs",
29 | Path: "/docs",
30 | },
31 | {
32 | Text: "Accordion",
33 | },
34 | },
35 | },
36 | }) {
37 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
38 | ShowcaseFile: showcase.AccordionDefault(),
39 | PreviewCodeFile: "accordion_default.templ",
40 | })
41 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
42 | Title: "Installation",
43 | ID: "installation",
44 | }) {
45 | @modules.ComponentUsage(modules.ComponentUsageProps{
46 | ComponentName: "accordion",
47 | })
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/internal/ui/pages/alert.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Alert() {
10 | @layouts.DocsLayout(
11 | "Alert",
12 | "Status message that displays contextual feedback or notifications.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | {
19 | ID: "examples",
20 | Text: "Examples",
21 | Children: []modules.TableOfContentsItem{
22 | {
23 | ID: "destructive",
24 | Text: "Destructive",
25 | },
26 | },
27 | },
28 | },
29 | ) {
30 | @modules.PageWrapper(modules.PageWrapperProps{
31 | Name: "Alert",
32 | Description: templ.Raw("Status message that displays contextual feedback or notifications."),
33 | Tailwind: true,
34 | Breadcrumbs: modules.Breadcrumbs{
35 | Items: []modules.BreadcrumbItem{
36 | {
37 | Text: "Docs",
38 | Path: "/docs",
39 | },
40 | {
41 | Text: "Alert",
42 | },
43 | },
44 | },
45 | }) {
46 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
47 | ShowcaseFile: showcase.AlertDefault(),
48 | PreviewCodeFile: "alert_default.templ",
49 | })
50 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
51 | Title: "Installation",
52 | ID: "installation",
53 | }) {
54 | @modules.ComponentUsage(modules.ComponentUsageProps{
55 | ComponentName: "alert",
56 | })
57 | }
58 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
59 | Title: "Examples",
60 | ID: "examples",
61 | }) {
62 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
63 | SectionName: "Destructive",
64 | ShowcaseFile: showcase.AlertDestructive(),
65 | PreviewCodeFile: "alert_destructive.templ",
66 | ID: "destructive",
67 | })
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/internal/ui/pages/aspect_ratio.templ:
--------------------------------------------------------------------------------
1 |
2 | package pages
3 |
4 | import "github.com/axzilla/templui/internal/ui/layouts"
5 | import "github.com/axzilla/templui/internal/ui/modules"
6 | import "github.com/axzilla/templui/internal/ui/showcase"
7 |
8 | templ AspectRatio() {
9 | @layouts.DocsLayout(
10 | "Aspect Ratio",
11 | "A component for maintaining consistent width-to-height ratios across different screen sizes.",
12 | []modules.TableOfContentsItem{
13 | {
14 | ID: "installation",
15 | Text: "Installation",
16 | },
17 | },
18 | ) {
19 | @modules.PageWrapper(modules.PageWrapperProps{
20 | Name: "Aspect Ratio",
21 | Description: templ.Raw("A component for maintaining consistent width-to-height ratios across different screen sizes."),
22 | Tailwind: true,
23 | Breadcrumbs: modules.Breadcrumbs{
24 | Items: []modules.BreadcrumbItem{
25 | {
26 | Text: "Docs",
27 | Path: "/docs",
28 | },
29 | {
30 | Text: "Aspect Ratio",
31 | },
32 | },
33 | },
34 | }) {
35 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
36 | ShowcaseFile: showcase.AspectRatioDefault(),
37 | PreviewCodeFile: "aspect_ratio_default.templ",
38 | })
39 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
40 | Title: "Installation",
41 | ID: "installation",
42 | }) {
43 | @modules.ComponentUsage(modules.ComponentUsageProps{
44 | ComponentName: "aspectratio",
45 | })
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/internal/ui/pages/calendar.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Calendar() {
10 | @layouts.DocsLayout(
11 | "Calendar",
12 | "A date field component that allows users to enter and edit date.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | },
19 | ) {
20 | @modules.PageWrapper(modules.PageWrapperProps{
21 | Name: "Calendar",
22 | Description: templ.Raw("A date field component that allows users to enter and edit date."),
23 | Tailwind: true,
24 | VanillaJS: true,
25 | Breadcrumbs: modules.Breadcrumbs{
26 | Items: []modules.BreadcrumbItem{
27 | {
28 | Text: "Docs",
29 | Path: "/docs",
30 | },
31 | {
32 | Text: "Calendar",
33 | },
34 | },
35 | },
36 | }) {
37 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
38 | ShowcaseFile: showcase.CalendarDefault(),
39 | PreviewCodeFile: "calendar_default.templ",
40 | })
41 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
42 | Title: "Installation",
43 | ID: "installation",
44 | }) {
45 | @modules.ComponentUsage(modules.ComponentUsageProps{
46 | ComponentName: "calendar",
47 | JSFiles: []string{"calendar"},
48 | })
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/internal/ui/pages/checkbox_card.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ CheckboxCard() {
10 | @layouts.DocsLayout(
11 | "Checkbox Card",
12 | "Selectable card component that combines a checkbox with rich content for option selection.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | },
19 | ) {
20 | @modules.PageWrapper(modules.PageWrapperProps{
21 | Name: "Checkbox Card",
22 | Description: templ.Raw("Selectable card component that combines a checkbox with rich content for option selection."),
23 | Tailwind: true,
24 | Breadcrumbs: modules.Breadcrumbs{
25 | Items: []modules.BreadcrumbItem{
26 | {
27 | Text: "Docs",
28 | Path: "/docs",
29 | },
30 | {
31 | Text: "Checkbox Card",
32 | },
33 | },
34 | },
35 | }) {
36 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
37 | ShowcaseFile: showcase.CheckboxCardDefault(),
38 | PreviewCodeFile: "checkbox_card_default.templ",
39 | })
40 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
41 | Title: "Installation",
42 | ID: "installation",
43 | }) {
44 | @modules.ComponentUsage(modules.ComponentUsageProps{
45 | ComponentName: "checkboxcard",
46 | })
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/internal/ui/pages/drawer.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Drawer() {
10 | @layouts.DocsLayout(
11 | "Drawer",
12 | "Side-anchored panel that slides in from screen edges.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | {
19 | Text: "Examples",
20 | ID: "examples",
21 | Children: []modules.TableOfContentsItem{
22 |
23 | {
24 | Text: "Positions",
25 | ID: "positions",
26 | },
27 | },
28 | },
29 | },
30 | ) {
31 | @modules.PageWrapper(modules.PageWrapperProps{
32 | Name: "Drawer",
33 | Description: templ.Raw("Side-anchored panel that slides in from screen edges."),
34 | Tailwind: true,
35 | VanillaJS: true,
36 | Breadcrumbs: modules.Breadcrumbs{
37 | Items: []modules.BreadcrumbItem{
38 | {
39 | Text: "Docs",
40 | Path: "/docs",
41 | },
42 | {
43 | Text: "Drawer",
44 | },
45 | },
46 | },
47 | }) {
48 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
49 | ShowcaseFile: showcase.DrawerDefault(),
50 | PreviewCodeFile: "drawer_default.templ",
51 | })
52 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
53 | Title: "Installation",
54 | ID: "installation",
55 | }) {
56 | @modules.ComponentUsage(modules.ComponentUsageProps{
57 | ComponentName: "drawer",
58 | JSFiles: []string{"drawer"},
59 | })
60 | }
61 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
62 | Title: "Examples",
63 | ID: "examples",
64 | }) {
65 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
66 | SectionName: "Positions",
67 | ShowcaseFile: showcase.DrawerPositions(),
68 | PreviewCodeFile: "drawer_positions.templ",
69 | ID: "positions",
70 | })
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/internal/ui/pages/dropdown.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Dropdown() {
10 | @layouts.DocsLayout(
11 | "Dropdown",
12 | "Floating menu for displaying a list of actions or options. Uses Popover for the popup.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | },
19 | ) {
20 | @modules.PageWrapper(modules.PageWrapperProps{
21 | Name: "Dropdown",
22 | Description: templ.Raw("Floating menu for displaying a list of actions or options. Uses Popover for the popup."),
23 | Tailwind: true,
24 | VanillaJS: true,
25 | Breadcrumbs: modules.Breadcrumbs{
26 | Items: []modules.BreadcrumbItem{
27 | {
28 | Text: "Docs",
29 | Path: "/docs",
30 | },
31 | {
32 | Text: "Dropdown",
33 | },
34 | },
35 | },
36 | }) {
37 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
38 | ShowcaseFile: showcase.DropdownDefault(),
39 | PreviewCodeFile: "dropdown_default.templ",
40 | })
41 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
42 | Title: "Installation",
43 | ID: "installation",
44 | }) {
45 | @modules.ComponentUsage(modules.ComponentUsageProps{
46 | ComponentName: "dropdown",
47 | JSFiles: []string{"dropdown", "popover"},
48 | })
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/internal/ui/pages/modal.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Modal() {
10 | @layouts.DocsLayout(
11 | "Modal",
12 | "Dialog overlay that requires user attention or interaction.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | },
19 | ) {
20 | @modules.PageWrapper(modules.PageWrapperProps{
21 | Name: "Modal",
22 | Description: templ.Raw("Dialog overlay that requires user attention or interaction."),
23 | Tailwind: true,
24 | VanillaJS: true,
25 | Breadcrumbs: modules.Breadcrumbs{
26 | Items: []modules.BreadcrumbItem{
27 | {
28 | Text: "Docs",
29 | Path: "/docs",
30 | },
31 | {
32 | Text: "Modal",
33 | },
34 | },
35 | },
36 | }) {
37 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
38 | ShowcaseFile: showcase.ModalDefault(),
39 | PreviewCodeFile: "modal_default.templ",
40 | })
41 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
42 | Title: "Installation",
43 | ID: "installation",
44 | }) {
45 | @modules.ComponentUsage(modules.ComponentUsageProps{
46 | ComponentName: "modal",
47 | JSFiles: []string{"modal"},
48 | })
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/internal/ui/pages/pagination.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Pagination() {
10 | @layouts.DocsLayout(
11 | "Pagination",
12 | "Navigation controls for moving between pages of content. HTMX ready.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | {
19 | Text: "Examples",
20 | ID: "examples",
21 | Children: []modules.TableOfContentsItem{
22 | {
23 | Text: "With Helper",
24 | ID: "with-helper",
25 | },
26 | },
27 | },
28 | },
29 | ) {
30 | @modules.PageWrapper(modules.PageWrapperProps{
31 | Name: "Pagination",
32 | Description: templ.Raw("Navigation controls for moving between pages of content. HTMX ready."),
33 | Tailwind: true,
34 | Breadcrumbs: modules.Breadcrumbs{
35 | Items: []modules.BreadcrumbItem{
36 | {
37 | Text: "Docs",
38 | Path: "/docs",
39 | },
40 | {
41 | Text: "Pagination",
42 | },
43 | },
44 | },
45 | }) {
46 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
47 | ShowcaseFile: showcase.PaginationDefault(),
48 | PreviewCodeFile: "pagination_default.templ",
49 | })
50 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
51 | Title: "Installation",
52 | ID: "installation",
53 | }) {
54 | @modules.ComponentUsage(modules.ComponentUsageProps{
55 | ComponentName: "pagination",
56 | })
57 | }
58 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
59 | Title: "Examples",
60 | ID: "examples",
61 | }) {
62 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
63 | SectionName: "With Helper",
64 | ShowcaseFile: showcase.PaginationWithHelper(),
65 | PreviewCodeFile: "pagination_with_helper.templ",
66 | ID: "with-helper",
67 | })
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/internal/ui/pages/radio_card.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ RadioCard() {
10 | @layouts.DocsLayout(
11 | "Radio Card",
12 | "Selectable card component that uses radio buttons for single-option selection.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | },
19 | ) {
20 | @modules.PageWrapper(modules.PageWrapperProps{
21 | Name: "Radio Card",
22 | Description: templ.Raw("Selectable card component that uses radio buttons for single-option selection."),
23 | Tailwind: true,
24 | Breadcrumbs: modules.Breadcrumbs{
25 | Items: []modules.BreadcrumbItem{
26 | {
27 | Text: "Docs",
28 | Path: "/docs",
29 | },
30 | {
31 | Text: "Radio Card",
32 | },
33 | },
34 | },
35 | }) {
36 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
37 | ShowcaseFile: showcase.RadioCardDefault(),
38 | PreviewCodeFile: "radio_card_default.templ",
39 | })
40 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
41 | Title: "Installation",
42 | ID: "installation",
43 | }) {
44 | @modules.ComponentUsage(modules.ComponentUsageProps{
45 | ComponentName: "radiocard",
46 | })
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/internal/ui/pages/table.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Table() {
10 | @layouts.DocsLayout(
11 | "Table",
12 | "Display tabular data with rich formatting and interaction options",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | },
19 | ) {
20 | @modules.PageWrapper(modules.PageWrapperProps{
21 | Name: "Table",
22 | Description: templ.Raw("Display tabular data with rich formatting and interaction options"),
23 | Tailwind: true,
24 | Breadcrumbs: modules.Breadcrumbs{
25 | Items: []modules.BreadcrumbItem{
26 | {
27 | Text: "Docs",
28 | Path: "/docs",
29 | },
30 | {
31 | Text: "Table",
32 | },
33 | },
34 | },
35 | }) {
36 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
37 | ShowcaseFile: showcase.Table(),
38 | PreviewCodeFile: "table.templ",
39 | })
40 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
41 | Title: "Installation",
42 | ID: "installation",
43 | }) {
44 | @modules.ComponentUsage(modules.ComponentUsageProps{
45 | ComponentName: "table",
46 | })
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/internal/ui/pages/tabs.templ:
--------------------------------------------------------------------------------
1 | package pages
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/ui/layouts"
5 | "github.com/axzilla/templui/internal/ui/modules"
6 | "github.com/axzilla/templui/internal/ui/showcase"
7 | )
8 |
9 | templ Tabs() {
10 | @layouts.DocsLayout(
11 | "Tabs",
12 | "Navigation interface that organizes content into sections.",
13 | []modules.TableOfContentsItem{
14 | {
15 | ID: "installation",
16 | Text: "Installation",
17 | },
18 | },
19 | ) {
20 | @modules.PageWrapper(modules.PageWrapperProps{
21 | Name: "Tabs",
22 | Description: templ.Raw("Navigation interface that organizes content into sections."),
23 | Tailwind: true,
24 | VanillaJS: true,
25 | Breadcrumbs: modules.Breadcrumbs{
26 | Items: []modules.BreadcrumbItem{
27 | {
28 | Text: "Docs",
29 | Path: "/docs",
30 | },
31 | {
32 | Text: "Tabs",
33 | },
34 | },
35 | },
36 | }) {
37 | @modules.ExampleWrapper(modules.ExampleWrapperProps{
38 | ShowcaseFile: showcase.TabsDefault(),
39 | PreviewCodeFile: "tabs_default.templ",
40 | })
41 | @modules.ContainerWrapper(modules.ContainerWrapperProps{
42 | Title: "Installation",
43 | ID: "installation",
44 | }) {
45 | @modules.ComponentUsage(modules.ComponentUsageProps{
46 | ComponentName: "tabs",
47 | JSFiles: []string{"tabs"},
48 | })
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/internal/ui/showcase/accordion_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/accordion"
4 |
5 | templ AccordionDefault() {
6 |
7 | @accordion.Accordion(accordion.Props{
8 | Class: "w-full",
9 | }) {
10 | @accordion.Item() {
11 | @accordion.Trigger() {
12 | Is it accessible?
13 | }
14 | @accordion.Content() {
15 | Yes. It adheres to the WAI-ARIA design pattern.
16 | }
17 | }
18 | @accordion.Item() {
19 | @accordion.Trigger() {
20 | Is it styled?
21 | }
22 | @accordion.Content() {
23 | Yes. It comes with default styles that matches the other components aesthetic.
24 | }
25 | }
26 | @accordion.Item() {
27 | @accordion.Trigger() {
28 | Is it animated?
29 | }
30 | @accordion.Content() {
31 | Yes. It is animated by default, but you can disable it if you prefer.
32 | }
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/internal/ui/showcase/alert_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/alert"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ AlertDefault() {
9 |
10 | @alert.Alert() {
11 | @icon.Rocket(icon.Props{Size: 16})
12 | @alert.Title() {
13 | Note
14 | }
15 | @alert.Description() {
16 | This is a default alert — check it out!
17 | }
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/alert_destructive.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/alert"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ AlertDestructive() {
9 |
10 | @alert.Alert(alert.Props{Variant: alert.VariantDestructive}) {
11 | @icon.TriangleAlert(icon.Props{Size: 16})
12 | @alert.Title() {
13 | Error
14 | }
15 | @alert.Description() {
16 | Your session has expired. Please log in again.
17 | }
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/aspect_ratio_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/aspectratio"
4 |
5 | templ AspectRatioDefault() {
6 | @aspectratio.AspectRatio(aspectratio.Props{
7 | Ratio: aspectratio.RatioVideo,
8 | Class: "rounded-md overflow-hidden",
9 | }) {
10 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/internal/ui/showcase/avatar_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/avatar"
4 |
5 | templ AvatarDefault() {
6 | @avatar.Avatar() {
7 | @avatar.Image(avatar.ImageProps{
8 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
9 | })
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/avatar_fallback.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/avatar"
4 |
5 | templ AvatarFallback() {
6 | @avatar.Avatar() {
7 | @avatar.Image(avatar.ImageProps{
8 | // simulate a broken image
9 | Src: "broken-image.jpg",
10 | })
11 | @avatar.Fallback() {
12 | { avatar.Initials("John Doe") }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/showcase/avatar_group.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/avatar"
4 |
5 | templ AvatarGroup() {
6 | @avatar.Group(avatar.GroupProps{
7 | Spacing: avatar.GroupSpacingLg,
8 | }) {
9 | @avatar.Avatar(avatar.Props{
10 | InGroup: true,
11 | }) {
12 | @avatar.Image(avatar.ImageProps{
13 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
14 | })
15 | }
16 | @avatar.Avatar(avatar.Props{
17 | InGroup: true,
18 | }) {
19 | @avatar.Image(avatar.ImageProps{
20 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
21 | })
22 | }
23 | @avatar.Avatar(avatar.Props{
24 | InGroup: true,
25 | }) {
26 | @avatar.Image(avatar.ImageProps{
27 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
28 | })
29 | }
30 | @avatar.GroupOverflow(2, avatar.Props{
31 | InGroup: true,
32 | }) {
33 | @avatar.Image(avatar.ImageProps{
34 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
35 | })
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/internal/ui/showcase/avatar_sizes.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/avatar"
4 |
5 | templ AvatarSizes() {
6 |
7 | @avatar.Avatar(avatar.Props{
8 | Size: avatar.SizeSm,
9 | }) {
10 | @avatar.Image(avatar.ImageProps{
11 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
12 | })
13 | }
14 | @avatar.Avatar() {
15 | @avatar.Image(avatar.ImageProps{
16 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
17 | })
18 | }
19 | @avatar.Avatar(avatar.Props{
20 | Size: avatar.SizeLg,
21 | }) {
22 | @avatar.Image(avatar.ImageProps{
23 | Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
24 | })
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/internal/ui/showcase/avatar_with_icon.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/avatar"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ AvatarWithIcon() {
9 | @avatar.Avatar(avatar.Props{
10 | Class: "bg-purple-300",
11 | }) {
12 | @icon.Camera(icon.Props{
13 | Size: 22,
14 | Color: "white",
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/internal/ui/showcase/badge_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/badge"
4 |
5 | templ BadgeDefault() {
6 | @badge.Badge() {
7 | Badge
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/badge_destructive.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/badge"
4 |
5 | templ BadgeDestructive() {
6 | @badge.Badge(badge.Props{
7 | Variant: badge.VariantDestructive,
8 | }) {
9 | Destructive
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/badge_outline.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/badge"
4 |
5 | templ BadgeOutline() {
6 | @badge.Badge(badge.Props{
7 | Variant: badge.VariantOutline,
8 | }) {
9 | Outline
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/badge_secondary.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/badge"
4 |
5 | templ BadgeSecondary() {
6 | @badge.Badge(badge.Props{
7 | Variant: badge.VariantSecondary,
8 | }) {
9 | Secondary
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/badge_with_icon.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/badge"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ BadgeWithIcon() {
9 | @badge.Badge(badge.Props{
10 | Class: "flex gap-1 items-center",
11 | }) {
12 | @icon.Rocket(icon.Props{Size: 14})
13 | With Icon
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/showcase/breadcrumb_custom_separator.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/breadcrumb"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ BreadcrumbCustomSeparator() {
9 | @breadcrumb.Breadcrumb() {
10 | @breadcrumb.List() {
11 | @breadcrumb.Item() {
12 | @breadcrumb.Link(breadcrumb.LinkProps{
13 | Href: "/",
14 | }) {
15 | Home
16 | }
17 | }
18 | @breadcrumb.Item() {
19 | @breadcrumb.Separator(breadcrumb.SeparatorProps{UseCustom: true}) {
20 | @icon.Slash(icon.Props{Size: 14})
21 | }
22 | @breadcrumb.Link(breadcrumb.LinkProps{
23 | Href: "/products",
24 | }) {
25 | Products
26 | }
27 | }
28 | @breadcrumb.Item() {
29 | @breadcrumb.Separator(breadcrumb.SeparatorProps{UseCustom: true}) {
30 | @icon.Slash(icon.Props{Size: 14})
31 | }
32 | @breadcrumb.Page(breadcrumb.ItemProps{Current: true}) {
33 | Category
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/internal/ui/showcase/breadcrumb_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/breadcrumb"
4 |
5 | templ BreadcrumbDefault() {
6 | @breadcrumb.Breadcrumb() {
7 | @breadcrumb.List() {
8 | @breadcrumb.Item() {
9 | @breadcrumb.Link(breadcrumb.LinkProps{
10 | Href: "/",
11 | }) {
12 | Home
13 | }
14 | }
15 | @breadcrumb.Item() {
16 | @breadcrumb.Separator()
17 | @breadcrumb.Link(breadcrumb.LinkProps{
18 | Href: "/docs",
19 | }) {
20 | Documentation
21 | }
22 | }
23 | @breadcrumb.Item() {
24 | @breadcrumb.Separator()
25 | @breadcrumb.Page(breadcrumb.ItemProps{Current: true}) {
26 | Components
27 | }
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/internal/ui/showcase/breadcrumb_responsive.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/breadcrumb"
4 |
5 | templ BreadcrumbResponsive() {
6 |
7 |
8 | @breadcrumb.Breadcrumb() {
9 | @breadcrumb.List() {
10 | @breadcrumb.Item() {
11 | @breadcrumb.Link(breadcrumb.LinkProps{
12 | Href: "/",
13 | }) {
14 | Home
15 | }
16 | }
17 | @breadcrumb.Separator()
18 | @breadcrumb.Item() {
19 | @breadcrumb.Link(breadcrumb.LinkProps{
20 | Href: "#",
21 | }) {
22 | ...
23 | }
24 | }
25 | @breadcrumb.Separator()
26 | @breadcrumb.Item() {
27 | @breadcrumb.Page(breadcrumb.ItemProps{Current: true}) {
28 | Current Page
29 | }
30 | }
31 | }
32 | }
33 |
34 |
35 |
36 | @breadcrumb.Breadcrumb() {
37 | @breadcrumb.List() {
38 | @breadcrumb.Item() {
39 | @breadcrumb.Link(breadcrumb.LinkProps{
40 | Href: "/",
41 | }) {
42 | Home
43 | }
44 | }
45 | @breadcrumb.Separator()
46 | @breadcrumb.Item() {
47 | @breadcrumb.Link(breadcrumb.LinkProps{
48 | Href: "/category",
49 | }) {
50 | Category
51 | }
52 | }
53 | @breadcrumb.Separator()
54 | @breadcrumb.Item() {
55 | @breadcrumb.Link(breadcrumb.LinkProps{
56 | Href: "/category/subcategory",
57 | }) {
58 | Subcategory
59 | }
60 | }
61 | @breadcrumb.Separator()
62 | @breadcrumb.Item() {
63 | @breadcrumb.Page(breadcrumb.ItemProps{Current: true}) {
64 | Current Page
65 | }
66 | }
67 | }
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/internal/ui/showcase/breadcrumb_with_icons.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/breadcrumb"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ BreadcrumbWithIcons() {
9 | @breadcrumb.Breadcrumb() {
10 | @breadcrumb.List() {
11 | @breadcrumb.Item() {
12 | @breadcrumb.Link(breadcrumb.LinkProps{
13 | Href: "/",
14 | }) {
15 | @icon.House(icon.Props{Size: 16})
16 | Home
17 | }
18 | }
19 | @breadcrumb.Item() {
20 | @breadcrumb.Separator()
21 | @breadcrumb.Link(breadcrumb.LinkProps{
22 | Href: "/docs",
23 | }) {
24 | @icon.FileText(icon.Props{Size: 16})
25 | Documentation
26 | }
27 | }
28 | @breadcrumb.Item() {
29 | @breadcrumb.Separator()
30 | @breadcrumb.Page(breadcrumb.ItemProps{Current: true}) {
31 | @icon.Component(icon.Props{Size: 16})
32 | Components
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ButtonDefault() {
6 | @button.Button() {
7 | Button
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_destructive.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ButtonDestructive() {
6 | @button.Button(button.Props{
7 | Variant: button.VariantDestructive,
8 | }) {
9 | Destructive
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_ghost.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ButtonGhost() {
6 | @button.Button(button.Props{
7 | Variant: button.VariantGhost,
8 | }) {
9 | Ghost
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_htmx_loading.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/spinner"
6 | )
7 |
8 | templ ButtonHtmxLoading() {
9 |
32 | }
33 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_icon.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ ButtonIcon() {
9 | @button.Button(button.Props{
10 | Size: button.SizeIcon,
11 | Variant: button.VariantOutline,
12 | }) {
13 | @icon.ChevronRight(icon.Props{Size: 16})
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_link.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ButtonLink() {
6 | @button.Button(button.Props{
7 | Variant: button.VariantLink,
8 | }) {
9 | Link
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_loading.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/spinner"
6 | )
7 |
8 | templ ButtonLoading() {
9 | @button.Button(button.Props{
10 | Disabled: true,
11 | Class: "flex items-center gap-2",
12 | }) {
13 | @spinner.Spinner(spinner.Props{
14 | Size: spinner.SizeSm,
15 | Color: "text-primary-foreground",
16 | })
17 | Please wait
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_outline.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ButtonOutline() {
6 | @button.Button(button.Props{
7 | Variant: button.VariantOutline,
8 | }) {
9 | Outline
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_primary.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ButtonPrimary() {
6 | @button.Button() {
7 | Primary
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_secondary.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ButtonSecondary() {
6 | @button.Button(button.Props{
7 | Variant: button.VariantSecondary,
8 | }) {
9 | Secondary
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/button_with_icon.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ ButtonWithIcon() {
9 | @button.Button(button.Props{
10 | Class: "flex gap-2 items-center",
11 | }) {
12 | @icon.Mail(icon.Props{Size: 16})
13 | Login with Email
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/showcase/calendar_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/calendar"
5 | "github.com/axzilla/templui/internal/components/card"
6 | )
7 |
8 | templ CalendarDefault() {
9 |
10 | @card.Card() {
11 | @card.Content() {
12 | @calendar.Calendar()
13 | }
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/internal/ui/showcase/card_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/card"
6 | "github.com/axzilla/templui/internal/components/input"
7 | "github.com/axzilla/templui/internal/components/label"
8 | "github.com/axzilla/templui/internal/components/selectbox"
9 | )
10 |
11 | templ CardDefault() {
12 |
13 | @card.Card() {
14 | @card.Header() {
15 | @card.Title() {
16 | Create Project
17 | }
18 | @card.Description() {
19 | Deploy your new project in one-click.
20 | }
21 | }
22 | @card.Content() {
23 |
24 |
25 | @label.Label(label.Props{
26 | For: "name",
27 | }) {
28 | Name
29 | }
30 | @input.Input(input.Props{
31 | ID: "name",
32 | Placeholder: "Enter project name",
33 | })
34 |
35 |
36 | @label.Label(label.Props{
37 | For: "service",
38 | }) {
39 | Service
40 | }
41 | @selectbox.SelectBox() {
42 | @selectbox.Trigger(selectbox.TriggerProps{
43 | ID: "service",
44 | }) {
45 | @selectbox.Value(selectbox.ValueProps{
46 | Placeholder: "Select",
47 | })
48 | }
49 | @selectbox.Content() {
50 | @selectbox.Group() {
51 | @selectbox.Item(selectbox.ItemProps{
52 | Value: "postgres",
53 | }) {
54 | PostgreSQL
55 | }
56 | @selectbox.Item(selectbox.ItemProps{
57 | Value: "mysql",
58 | }) {
59 | MySQL
60 | }
61 | @selectbox.Item(selectbox.ItemProps{
62 | Value: "sqlite",
63 | }) {
64 | SQLite
65 | }
66 | }
67 | }
68 | }
69 |
70 |
71 | }
72 | @card.Footer(card.FooterProps{
73 | Class: "flex justify-between",
74 | }) {
75 | @button.Button(button.Props{
76 | Variant: button.VariantSecondary,
77 | }) {
78 | Cancel
79 | }
80 | @button.Button() {
81 | Deploy
82 | }
83 | }
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/internal/ui/showcase/card_image_bottom.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/aspectratio"
5 | "github.com/axzilla/templui/internal/components/button"
6 | "github.com/axzilla/templui/internal/components/card"
7 | )
8 |
9 | templ CardImageBottom() {
10 |
11 | @card.Card() {
12 | @card.Header() {
13 | @card.Title() {
14 | Featured Card
15 | }
16 | @card.Description() {
17 | With bottom image
18 | }
19 | }
20 | @card.Content() {
21 |
This card shows bottom image usage.
22 | }
23 | @card.Footer() {
24 | @button.Button() {
25 | Learn more
26 | }
27 | }
28 | @card.Media(card.MediaProps{
29 | ID: "bottom-media",
30 | Alt: "Card image",
31 | Position: card.MediaPositionBottom,
32 | AspectRatio: aspectratio.RatioVideo,
33 | Src: "/assets/img/card_placeholder.jpeg",
34 | },
35 | )
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/internal/ui/showcase/card_image_left.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/aspectratio"
5 | "github.com/axzilla/templui/internal/components/card"
6 | )
7 |
8 | templ CardImageLeft() {
9 |
10 | @card.Card() {
11 | @card.Horizontal() {
12 | @card.Media(card.MediaProps{
13 | ID: "left-media",
14 | Alt: "Left side image",
15 | Position: card.MediaPositionLeft,
16 | Width: card.MediaWidthThird,
17 | AspectRatio: aspectratio.RatioAuto,
18 | Src: "/assets/img/card_placeholder.jpeg",
19 | },
20 | )
21 |
22 | @card.Header() {
23 | @card.Title() {
24 | Side Image Card
25 | }
26 | @card.Description() {
27 | With left-aligned image
28 | }
29 | }
30 | @card.Content() {
31 |
This card demonstrates the left image layout.
32 | }
33 |
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/internal/ui/showcase/card_image_right.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/aspectratio"
5 | "github.com/axzilla/templui/internal/components/card"
6 | )
7 |
8 | templ CardImageRight() {
9 |
10 | @card.Card() {
11 | @card.Horizontal() {
12 |
13 | @card.Header() {
14 | @card.Title() {
15 | Side Image Card
16 | }
17 | @card.Description() {
18 | With right-aligned image
19 | }
20 | }
21 | @card.Content() {
22 |
This card demonstrates the right image layout.
23 | }
24 |
25 | @card.Media(card.MediaProps{
26 | ID: "right-media",
27 | Alt: "Right side image",
28 | Position: card.MediaPositionRight,
29 | Width: card.MediaWidthThird,
30 | AspectRatio: aspectratio.RatioAuto,
31 | Src: "/assets/img/card_placeholder.jpeg",
32 | },
33 | )
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/internal/ui/showcase/card_image_top.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/aspectratio"
5 | "github.com/axzilla/templui/internal/components/button"
6 | "github.com/axzilla/templui/internal/components/card"
7 | )
8 |
9 | templ CardImageTop() {
10 |
11 | @card.Card() {
12 | @card.Media(card.MediaProps{
13 | ID: "top-media",
14 | Alt: "Card image",
15 | Position: card.MediaPositionTop,
16 | AspectRatio: aspectratio.RatioVideo,
17 | Src: "/assets/img/card_placeholder.jpeg",
18 | },
19 | )
20 | @card.Header() {
21 | @card.Title() {
22 | Featured Card
23 | }
24 | @card.Description() {
25 | With top image
26 | }
27 | }
28 | @card.Content() {
29 |
This card shows top image usage.
30 | }
31 | @card.Footer() {
32 | @button.Button() {
33 | Learn more
34 | }
35 | }
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/internal/ui/showcase/carousel_autoplay.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/carousel"
4 |
5 | templ CarouselAutoplay() {
6 | @carousel.Carousel(carousel.Props{
7 | Autoplay: true,
8 | Interval: 3000,
9 | Loop: true,
10 | Class: "rounded-md",
11 | }) {
12 | @carousel.Content() {
13 | @carousel.Item() {
14 | @CarouselAutoplaySlide("Slide 1", "This is the first slide", "bg-blue-500")
15 | }
16 | @carousel.Item() {
17 | @CarouselAutoplaySlide("Slide 2", "This is the second slide", "bg-green-500")
18 | }
19 | @carousel.Item() {
20 | @CarouselAutoplaySlide("Slide 3", "This is the third slide", "bg-purple-500")
21 | }
22 | }
23 | @carousel.Previous()
24 | @carousel.Next()
25 | @carousel.Indicators(carousel.IndicatorsProps{
26 | Count: 3,
27 | })
28 | }
29 | }
30 |
31 | templ CarouselAutoplaySlide(title, description, bg string) {
32 |
33 |
34 |
{ title }
35 |
{ description }
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/internal/ui/showcase/carousel_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/carousel"
4 |
5 | templ CarouselDefault() {
6 | @carousel.Carousel(carousel.Props{
7 | Class: "rounded-md",
8 | }) {
9 | @carousel.Content() {
10 | @carousel.Item() {
11 | @CarouselSlide("Slide 1", "This is the first slide", "bg-blue-500")
12 | }
13 | @carousel.Item() {
14 | @CarouselSlide("Slide 2", "This is the second slide", "bg-green-500")
15 | }
16 | @carousel.Item() {
17 | @CarouselSlide("Slide 3", "This is the third slide", "bg-purple-500")
18 | }
19 | }
20 | @carousel.Previous()
21 | @carousel.Next()
22 | @carousel.Indicators(carousel.IndicatorsProps{
23 | Count: 3,
24 | })
25 | }
26 | }
27 |
28 | templ CarouselSlide(title, description, bg string) {
29 |
30 |
31 |
{ title }
32 |
{ description }
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/internal/ui/showcase/carousel_minimal.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/carousel"
4 |
5 | templ CarouselMinimal() {
6 | @carousel.Carousel(carousel.Props{
7 | Interval: 2000,
8 | Autoplay: true,
9 | Loop: true,
10 | Class: "rounded-md",
11 | }) {
12 | @carousel.Content() {
13 | @carousel.Item() {
14 | @CarouselMinimalSlide("Slide 1", "This is the first slide", "bg-blue-500")
15 | }
16 | @carousel.Item() {
17 | @CarouselMinimalSlide("Slide 2", "This is the second slide", "bg-green-500")
18 | }
19 | @carousel.Item() {
20 | @CarouselMinimalSlide("Slide 3", "This is the third slide", "bg-purple-500")
21 | }
22 | }
23 | }
24 | }
25 |
26 | templ CarouselMinimalSlide(title, description, bg string) {
27 |
28 |
29 |
{ title }
30 |
{ description }
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/internal/ui/showcase/carousel_with_images.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/aspectratio"
5 | "github.com/axzilla/templui/internal/components/carousel"
6 | )
7 |
8 | templ CarouselWithImages() {
9 | @carousel.Carousel(carousel.Props{
10 | Autoplay: true,
11 | Interval: 5000,
12 | Loop: true,
13 | Class: "rounded-md overflow-hidden shadow-md",
14 | }) {
15 | @carousel.Content() {
16 | @carousel.Item() {
17 | @ImageSlide("/assets/img/demo/carousel-1.jpeg", "Image 1")
18 |
19 |
Nature landscape example 1
20 |
21 | }
22 | @carousel.Item() {
23 | @ImageSlide("/assets/img/demo/carousel-2.jpeg", "Image 2")
24 |
25 |
Nature landscape example 2
26 |
27 | }
28 | @carousel.Item() {
29 | @ImageSlide("/assets/img/demo/carousel-3.jpeg", "Image 3")
30 |
31 |
Nature landscape example 3
32 |
33 | }
34 | }
35 | @carousel.Previous()
36 | @carousel.Next()
37 | @carousel.Indicators(carousel.IndicatorsProps{
38 | Count: 3,
39 | })
40 | }
41 | }
42 |
43 | templ ImageSlide(src string, alt string) {
44 | @aspectratio.AspectRatio(aspectratio.Props{
45 | Ratio: aspectratio.RatioVideo,
46 | }) {
47 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_area.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartArea() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{3, 9, 3, 12, 7, 8},
20 | Tension: 0.5,
21 | BorderWidth: 1,
22 | Fill: true,
23 | },
24 | },
25 | },
26 | })
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_area_linear.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartAreaLinear() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{3, 9, 3, 12, 7, 8},
20 | BorderWidth: 1,
21 | Fill: true,
22 | },
23 | },
24 | },
25 | })
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_area_stacked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartAreaStacked() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{3, 9, 3, 12, 7, 8},
20 | BorderWidth: 1,
21 | Fill: true,
22 | Tension: 0.5,
23 | Label: "Mobile",
24 | },
25 | {
26 | Data: []float64{7, 16, 5, 20, 14, 15},
27 | BorderWidth: 1,
28 | Fill: true,
29 | Tension: 0.5,
30 | Label: "Mobile",
31 | },
32 | },
33 | },
34 | })
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_area_step.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartAreaStep() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{3, 9, 3, 12, 7, 8},
20 | BorderWidth: 1,
21 | Fill: true,
22 | Stepped: true,
23 | },
24 | },
25 | },
26 | })
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_bar_horizontal.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartBarHorizontal() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantBar,
13 | Horizontal: true,
14 | ShowXGrid: true,
15 | ShowYLabels: true,
16 | Data: chart.Data{
17 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
18 | Datasets: []chart.Dataset{
19 | {
20 | Data: []float64{12, 19, 12, 5, 2, 3},
21 | },
22 | },
23 | },
24 | })
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_bar_multiple.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartBarMultiple() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantBar,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Label: "Mobile",
20 | Data: []float64{12, 19, 12, 5, 2, 3},
21 | },
22 | {
23 | Label: "Desktop",
24 | Data: []float64{3, 9, 18, 3, 21, 13},
25 | },
26 | },
27 | },
28 | })
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_bar_negative.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartBarNegative() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantBar,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{12, 19, -12, 5, -2, 3},
20 | },
21 | },
22 | },
23 | })
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_bar_stacked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartBarStacked() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantBar,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Stacked: true,
16 | Data: chart.Data{
17 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
18 | Datasets: []chart.Dataset{
19 | {
20 | Label: "Mobile",
21 | Data: []float64{12, 19, 12, 5, 2, 3},
22 | },
23 | {
24 | Label: "Desktop",
25 | Data: []float64{3, 9, 18, 3, 21, 13},
26 | },
27 | },
28 | },
29 | })
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartDefault() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantBar,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{12, 19, 12, 5, 2, 3},
20 | },
21 | },
22 | },
23 | })
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_doughnut.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartDoughnut() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantDoughnut,
13 | Data: chart.Data{
14 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
15 | Datasets: []chart.Dataset{
16 | {
17 | Data: []float64{7, 16, 5, 20, 14, 15},
18 | },
19 | },
20 | },
21 | })
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_doughnut_legend.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartDoughnutLegend() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantDoughnut,
13 | ShowLegend: true,
14 | Data: chart.Data{
15 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
16 | Datasets: []chart.Dataset{
17 | {
18 | Data: []float64{3, 9, 3, 12, 7, 8},
19 | Label: "Mobile",
20 | },
21 | },
22 | },
23 | })
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_doughnut_stacked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartDoughnutStacked() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantDoughnut,
13 | Data: chart.Data{
14 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
15 | Datasets: []chart.Dataset{
16 | {
17 | Data: []float64{3, 9, 3, 12, 7, 8},
18 | Label: "Mobile",
19 | },
20 | {
21 | Data: []float64{7, 16, 5, 20, 14, 15},
22 | Label: "Desktop",
23 | },
24 | },
25 | },
26 | })
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_line.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartLine() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{12, 3, 9, 3, 12, 7},
20 | Tension: 0.5,
21 | },
22 | },
23 | },
24 | })
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_line_linear.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartLineLinear() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{12, 3, 9, 3, 12, 7},
20 | },
21 | },
22 | },
23 | })
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_line_multiple.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartLineMultiple() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Label: "Mobile",
20 | Data: []float64{12, 3, 9, 3, 12, 7},
21 | Tension: 0.5,
22 | },
23 | {
24 | Label: "Desktop",
25 | Data: []float64{7, 14, 12, 21, 2, 9},
26 | Tension: 0.5,
27 | },
28 | },
29 | },
30 | })
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_line_step.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartLineStep() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantLine,
13 | ShowYGrid: true,
14 | ShowXLabels: true,
15 | Data: chart.Data{
16 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
17 | Datasets: []chart.Dataset{
18 | {
19 | Data: []float64{12, 3, 9, 3, 12, 7},
20 | Stepped: true,
21 | },
22 | },
23 | },
24 | })
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_pie.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartPie() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantPie,
13 | Data: chart.Data{
14 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
15 | Datasets: []chart.Dataset{
16 | {
17 | Data: []float64{3, 9, 3, 12, 7, 8},
18 | },
19 | },
20 | },
21 | })
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_pie_legend.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartPieLegend() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantPie,
13 | ShowLegend: true,
14 | Data: chart.Data{
15 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
16 | Datasets: []chart.Dataset{
17 | {
18 | Data: []float64{7, 16, 5, 20, 14, 15},
19 | },
20 | },
21 | },
22 | })
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_pie_stacked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartPieStacked() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantPie,
13 | Data: chart.Data{
14 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
15 | Datasets: []chart.Dataset{
16 | {
17 | Data: []float64{3, 9, 3, 12, 7, 8},
18 | Label: "Mobile",
19 | },
20 | {
21 | Data: []float64{7, 16, 5, 20, 14, 15},
22 | Label: "Desktop",
23 | },
24 | },
25 | },
26 | })
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_radar.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ ChartRadar() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantRadar,
13 | Data: chart.Data{
14 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
15 | Datasets: []chart.Dataset{
16 | {
17 | Data: []float64{3, 9, 3, 12, 7, 8},
18 | },
19 | },
20 | },
21 | })
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internal/ui/showcase/chart_radar_stacked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/card"
5 | "github.com/axzilla/templui/internal/components/chart"
6 | )
7 |
8 | templ CharRadarStacked() {
9 | @card.Card(card.Props{Class: "max-w-sm"}) {
10 | @card.Content() {
11 | @chart.Chart(chart.Props{
12 | Variant: chart.VariantRadar,
13 | Data: chart.Data{
14 | Labels: []string{"Jan", "Feb", "March", "April", "May", "June"},
15 | Datasets: []chart.Dataset{
16 | {
17 | Data: []float64{15, 9, 3, 12, 7, 8},
18 | Label: "Mobile",
19 | },
20 | {
21 | Data: []float64{7, 16, 5, 20, 14, 15},
22 | Label: "Desktop",
23 | },
24 | },
25 | },
26 | })
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/internal/ui/showcase/checkbox_card_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/icon"
4 | import "github.com/axzilla/templui/internal/components/checkboxcard"
5 |
6 | templ CheckboxCardDefault() {
7 |
8 | @checkboxcard.CheckboxCard(checkboxcard.Props{
9 | ID: "feature-analytics",
10 | },
11 | ) {
12 | @checkboxcard.Header() {
13 |
14 |
15 | @icon.ChartBar(icon.Props{Size: 20})
16 |
17 |
Analytics
18 |
19 | }
20 | @checkboxcard.Description() {
21 | Real-time data analytics and reporting tools
22 | }
23 | @checkboxcard.Footer() {
24 | @radioCardPriceFooter("$5/month")
25 | }
26 | }
27 | @checkboxcard.CheckboxCard(checkboxcard.Props{
28 | ID: "feature-storage",
29 | },
30 | ) {
31 | @checkboxcard.Header() {
32 |
33 |
34 | @icon.Cloud(icon.Props{Size: 20})
35 |
36 |
Cloud Storage
37 |
38 | }
39 | @checkboxcard.Description() {
40 | Secure file storage with 100GB capacity
41 | }
42 | @checkboxcard.Footer() {
43 | @radioCardPriceFooter("$3/month")
44 | }
45 | }
46 | @checkboxcard.CheckboxCard(checkboxcard.Props{
47 | ID: "feature-api",
48 | Disabled: true,
49 | }) {
50 | @checkboxcard.Header() {
51 |
52 |
53 | @icon.Code(icon.Props{Size: 20})
54 |
55 |
API Access
56 |
57 | }
58 | @checkboxcard.Description() {
59 | Full access to our developer API endpoints
60 | }
61 | @checkboxcard.Footer() {
62 | @radioCardPriceFooter("$8/month")
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/internal/ui/showcase/checkbox_checked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/checkbox"
4 |
5 | templ CheckboxChecked() {
6 | @checkbox.Checkbox(checkbox.Props{
7 | Checked: true,
8 | },
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/internal/ui/showcase/checkbox_custom_icon.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/checkbox"
5 | "github.com/axzilla/templui/internal/components/icon"
6 | )
7 |
8 | templ CheckboxCustomIcon() {
9 | @checkbox.Checkbox(checkbox.Props{
10 | Icon: icon.Plus(icon.Props{Size: 12}),
11 | Checked: true,
12 | },
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/internal/ui/showcase/checkbox_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/checkbox"
4 |
5 | templ CheckboxDefault() {
6 | @checkbox.Checkbox()
7 | }
8 |
--------------------------------------------------------------------------------
/internal/ui/showcase/checkbox_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/checkbox"
5 | "github.com/axzilla/templui/internal/components/label"
6 | )
7 |
8 | templ CheckboxDisabled() {
9 |
10 | @checkbox.Checkbox(checkbox.Props{
11 | Disabled: true,
12 | ID: "checkbox-disabled",
13 | },
14 | )
15 | @label.Label(label.Props{
16 | For: "checkbox-disabled",
17 | }) {
18 | Accept terms and conditions
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/internal/ui/showcase/checkbox_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/checkbox"
5 | "github.com/axzilla/templui/internal/components/form"
6 | )
7 |
8 | templ CheckboxForm() {
9 |
10 | @form.Item() {
11 | @form.ItemFlex() {
12 | @checkbox.Checkbox(
13 | checkbox.Props{
14 | Name: "interests",
15 | Value: "design",
16 | ID: "c1",
17 | Checked: true,
18 | },
19 | )
20 | @form.Label(form.LabelProps{
21 | For: "c1",
22 | }) {
23 | Dessign and UX
24 | }
25 | }
26 | @form.ItemFlex() {
27 | @checkbox.Checkbox(checkbox.Props{
28 | Name: "interests",
29 | Value: "development",
30 | ID: "c2",
31 | Disabled: true,
32 | })
33 | @form.Label(form.LabelProps{
34 | For: "c2",
35 | }) {
36 | Development (Coming Soon)
37 | }
38 | }
39 | @form.ItemFlex() {
40 | @checkbox.Checkbox(checkbox.Props{
41 | Name: "interests",
42 | Value: "marketing",
43 | ID: "c3",
44 | })
45 | @form.Label(form.LabelProps{
46 | For: "c3",
47 | }) {
48 | Business and Marketing
49 | }
50 | }
51 | @form.Description() {
52 | Choose all areas that interest you.
53 | }
54 | @form.Message(form.MessageProps{
55 | Variant: form.MessageVariantError,
56 | }) {
57 | Please select at least one interest.
58 | }
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/internal/ui/showcase/checkbox_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/checkbox"
5 | "github.com/axzilla/templui/internal/components/label"
6 | )
7 |
8 | templ CheckboxWithLabel() {
9 |
10 | @checkbox.Checkbox(checkbox.Props{
11 | ID: "checkbox-with-label",
12 | })
13 | @label.Label(label.Props{
14 | For: "checkbox-with-label",
15 | }) {
16 | Accept terms and conditions
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/internal/ui/showcase/code_copy_button.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/code"
4 |
5 | templ CodeCopyButton() {
6 |
7 | @code.Code(code.Props{
8 | Language: "go",
9 | ShowCopyButton: true,
10 | }) {
11 | { `fmt.Println("Hello, World!")` }
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/internal/ui/showcase/code_custom_size.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/code"
4 |
5 | templ CodeCustomSize() {
6 |
7 | @code.Code(code.Props{
8 | Language: "go",
9 | ShowCopyButton: true,
10 | Size: code.SizeSm,
11 | }) {
12 | { `package main
13 |
14 | import (
15 | "fmt"
16 | "log"
17 | "net/http"
18 | )
19 |
20 | func main() {
21 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
22 | fmt.Fprintf(w, "Hello, World!")
23 | })
24 |
25 | fmt.Println("Server starting on :3000...")
26 | if err := http.ListenAndServe(":3000", nil); err != nil {
27 | log.Fatal(err)
28 | }
29 | }` }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/internal/ui/showcase/code_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/code"
4 |
5 | templ CodeDefault() {
6 |
7 | @code.Code(code.Props{
8 | Language: "go",
9 | }) {
10 | { `fmt.Println("Hello, World!")` }
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/internal/ui/showcase/date_picker_custom_placeholder.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/datepicker"
4 |
5 | templ DatePickerCustomPlaceholder() {
6 |
7 | @datepicker.DatePicker(datepicker.Props{
8 | Placeholder: "When is your birthday?",
9 | })
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/date_picker_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/datepicker"
4 |
5 | templ DatePickerDefault() {
6 |
7 | @datepicker.DatePicker()
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/date_picker_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/datepicker"
4 |
5 | templ DatePickerDisabled() {
6 |
7 | @datepicker.DatePicker(datepicker.Props{
8 | Disabled: true,
9 | })
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/date_picker_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/datepicker"
5 | "github.com/axzilla/templui/internal/components/form"
6 | )
7 |
8 | templ DatePickerForm() {
9 |
10 | @form.Item() {
11 | @form.Label(form.LabelProps{
12 | For: "date-picker-form",
13 | }) {
14 | Select a date
15 | }
16 | @datepicker.DatePicker(datepicker.Props{
17 | ID: "date-picker-form",
18 | HasError: true,
19 | })
20 | @form.Description() {
21 | Select a date from the calendar.
22 | }
23 | @form.Message(form.MessageProps{
24 | Variant: form.MessageVariantError,
25 | }) {
26 | Select a valid date
27 | }
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/internal/ui/showcase/date_picker_selected_date.templ:
--------------------------------------------------------------------------------
1 |
2 | package showcase
3 |
4 | import (
5 | "github.com/axzilla/templui/internal/components/datepicker"
6 | "time"
7 | )
8 |
9 | templ DatePickerSelectedDate() {
10 |
11 | @datepicker.DatePicker(datepicker.Props{
12 | Value: time.Now(),
13 | })
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/showcase/date_picker_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/datepicker"
4 |
5 | templ DatePickerWithLabel() {
6 |
7 | // @label.Label(label.Props{
8 | // For: "date-picker-with-label",
9 | // }) {
10 | // Pick a date
11 | // }
12 | @datepicker.DatePicker(datepicker.Props{
13 | ID: "xxxx",
14 | })
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/internal/ui/showcase/drawer_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/card"
6 | "github.com/axzilla/templui/internal/components/drawer"
7 | "github.com/axzilla/templui/internal/components/input"
8 | )
9 |
10 | templ DrawerDefault() {
11 | @drawer.Drawer() {
12 | @drawer.Trigger(drawer.TriggerProps{Class: "mb-4"}) {
13 | @button.Button() {
14 | Open
15 | }
16 | }
17 | @drawer.Content(drawer.ContentProps{
18 | Position: drawer.PositionRight,
19 | }) {
20 | @drawer.Header() {
21 | @drawer.Title() {
22 | Account
23 | }
24 | @drawer.Description() {
25 | Make changes to your account here. Click save when you are done.
26 | }
27 | }
28 | @card.Card() {
29 | @card.Content() {
30 |
31 | @input.Input(input.Props{
32 | Type: input.TypeText,
33 | Placeholder: "Name",
34 | ID: "name",
35 | Value: "John Doe",
36 | })
37 | @input.Input(input.Props{
38 | Type: input.TypeText,
39 | Placeholder: "Username",
40 | ID: "username",
41 | Value: "@johndoe",
42 | })
43 |
44 | }
45 | }
46 | @drawer.Footer() {
47 | @drawer.Close() {
48 | Cancel
49 | }
50 | @button.Button() {
51 | Save
52 | }
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/internal/ui/showcase/embed.go:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "embed"
5 | )
6 |
7 | //go:embed *.templ
8 | var TemplFiles embed.FS
9 |
--------------------------------------------------------------------------------
/internal/ui/showcase/icon_colored.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/icon"
4 |
5 | templ IconColored() {
6 | @icon.Heart(icon.Props{Size: 24, Color: "red"})
7 | }
8 |
--------------------------------------------------------------------------------
/internal/ui/showcase/icon_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/icon"
4 |
5 | templ IconDefault() {
6 | @icon.User()
7 | }
8 |
--------------------------------------------------------------------------------
/internal/ui/showcase/icon_filled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/icon"
4 |
5 | templ IconFilled() {
6 | @icon.Triangle(icon.Props{Size: 24, Fill: "orange", Stroke: "orange"})
7 | }
8 |
--------------------------------------------------------------------------------
/internal/ui/showcase/icon_sizes.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/icon"
4 |
5 | templ IconSizes() {
6 |
7 | @icon.House()
8 | @icon.House(icon.Props{Size: 32})
9 | @icon.House(icon.Props{Size: 48})
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/input"
4 |
5 | templ InputDefault() {
6 |
7 | @input.Input(input.Props{
8 | Type: input.TypeEmail,
9 | Placeholder: "Email",
10 | },
11 | )
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/input"
4 |
5 | templ InputDisabled() {
6 |
7 | @input.Input(input.Props{
8 | Type: input.TypeEmail,
9 | Placeholder: "Email",
10 | Disabled: true,
11 | },
12 | )
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_file.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/input"
4 |
5 | templ InputFile() {
6 |
7 | @input.Input(input.Props{Type: input.TypeFile})
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/input"
6 | )
7 |
8 | templ InputForm() {
9 |
10 | @form.Item() {
11 | @form.Label(form.LabelProps{
12 | For: "email-form",
13 | }) {
14 | Email
15 | }
16 | @input.Input(input.Props{
17 | ID: "email-form",
18 | Type: input.TypeEmail,
19 | Placeholder: "m@example.com",
20 | HasError: true,
21 | })
22 | @form.Description() {
23 | Enter your email address for notifications.
24 | }
25 | @form.Message(form.MessageProps{
26 | Variant: form.MessageVariantError,
27 | }) {
28 | Please enter a valid email address
29 | }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_otp_custom_length.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/inputotp"
4 |
5 | templ InputOTPCustomLength() {
6 | @inputotp.InputOTP(inputotp.Props{
7 | ID: "otp-custom-length",
8 | }) {
9 | @inputotp.Group() {
10 | @inputotp.Slot(inputotp.SlotProps{
11 | Index: 0,
12 | })
13 | @inputotp.Slot(inputotp.SlotProps{
14 | Index: 1,
15 | })
16 | @inputotp.Slot(inputotp.SlotProps{
17 | Index: 2,
18 | })
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_otp_custom_styling.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/inputotp"
4 |
5 | templ InputOTPCustomStyling() {
6 | @inputotp.InputOTP(inputotp.Props{
7 | ID: "otp-styled",
8 | }) {
9 | @inputotp.Group(inputotp.GroupProps{
10 | Class: "gap-3",
11 | }) {
12 | @inputotp.Slot(inputotp.SlotProps{
13 | Index: 0,
14 | Class: "w-12 h-14 bg-primary/10 border-primary text-lg font-bold",
15 | })
16 | @inputotp.Slot(inputotp.SlotProps{
17 | Index: 1,
18 | Class: "w-12 h-14 bg-primary/10 border-primary text-lg font-bold",
19 | })
20 | @inputotp.Slot(inputotp.SlotProps{
21 | Index: 2,
22 | Class: "w-12 h-14 bg-primary/10 border-primary text-lg font-bold",
23 | })
24 | @inputotp.Separator(inputotp.SeparatorProps{
25 | Class: "text-2xl font-bold text-primary",
26 | }) {
27 | :
28 | }
29 | @inputotp.Slot(inputotp.SlotProps{
30 | Index: 3,
31 | Class: "w-12 h-14 bg-primary/10 border-primary text-lg font-bold",
32 | })
33 | @inputotp.Slot(inputotp.SlotProps{
34 | Index: 4,
35 | Class: "w-12 h-14 bg-primary/10 border-primary text-lg font-bold",
36 | })
37 | @inputotp.Slot(inputotp.SlotProps{
38 | Index: 5,
39 | Class: "w-12 h-14 bg-primary/10 border-primary text-lg font-bold",
40 | })
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_otp_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/inputotp"
4 |
5 | templ InputOTPDefault() {
6 | @inputotp.InputOTP() {
7 | @inputotp.Group() {
8 | @inputotp.Slot(inputotp.SlotProps{
9 | Index: 0,
10 | })
11 | @inputotp.Slot(inputotp.SlotProps{
12 | Index: 1,
13 | })
14 | @inputotp.Slot(inputotp.SlotProps{
15 | Index: 2,
16 | })
17 | @inputotp.Slot(inputotp.SlotProps{
18 | Index: 3,
19 | })
20 | @inputotp.Slot(inputotp.SlotProps{
21 | Index: 4,
22 | })
23 | @inputotp.Slot(inputotp.SlotProps{
24 | Index: 5,
25 | })
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_otp_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/inputotp"
6 | )
7 |
8 | templ InputOTPForm() {
9 | @form.Item() {
10 | @form.Label(form.LabelProps{
11 | For: "otp-form",
12 | }) {
13 | Verification Code
14 | }
15 | @inputotp.InputOTP(inputotp.Props{
16 | ID: "otp-form",
17 | Required: true,
18 | HasError: true,
19 | }) {
20 | @inputotp.Group() {
21 | @inputotp.Slot(inputotp.SlotProps{
22 | Index: 0,
23 | })
24 | @inputotp.Slot(inputotp.SlotProps{
25 | Index: 1,
26 | })
27 | @inputotp.Slot(inputotp.SlotProps{
28 | Index: 2,
29 | })
30 | @inputotp.Separator()
31 | @inputotp.Slot(inputotp.SlotProps{
32 | Index: 3,
33 | })
34 | @inputotp.Slot(inputotp.SlotProps{
35 | Index: 4,
36 | })
37 | @inputotp.Slot(inputotp.SlotProps{
38 | Index: 5,
39 | })
40 | }
41 | }
42 | @form.Description() {
43 | Enter the 6-digit code sent to your phone
44 | }
45 | @form.Message(form.MessageProps{
46 | Variant: form.MessageVariantError,
47 | }) {
48 | Invalid verification code.
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_otp_password_type.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/inputotp"
4 |
5 | templ InputOTPPasswordType() {
6 | @inputotp.InputOTP(inputotp.Props{
7 | ID: "otp-password",
8 | }) {
9 | @inputotp.Group() {
10 | @inputotp.Slot(inputotp.SlotProps{
11 | Index: 0,
12 | Type: "password",
13 | })
14 | @inputotp.Slot(inputotp.SlotProps{
15 | Index: 1,
16 | Type: "password",
17 | })
18 | @inputotp.Slot(inputotp.SlotProps{
19 | Index: 2,
20 | Type: "password",
21 | })
22 | @inputotp.Separator()
23 | @inputotp.Slot(inputotp.SlotProps{
24 | Index: 3,
25 | Type: "password",
26 | })
27 | @inputotp.Slot(inputotp.SlotProps{
28 | Index: 4,
29 | Type: "password",
30 | })
31 | @inputotp.Slot(inputotp.SlotProps{
32 | Index: 5,
33 | Type: "password",
34 | })
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_otp_placeholder.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/inputotp"
4 |
5 | templ InputOTPPlaceholder() {
6 | @inputotp.InputOTP() {
7 | @inputotp.Group() {
8 | @inputotp.Slot(inputotp.SlotProps{
9 | Index: 0,
10 | Placeholder: "•",
11 | })
12 | @inputotp.Slot(inputotp.SlotProps{
13 | Index: 1,
14 | Placeholder: "•",
15 | })
16 | @inputotp.Slot(inputotp.SlotProps{
17 | Index: 2,
18 | Placeholder: "•",
19 | })
20 | @inputotp.Separator()
21 | @inputotp.Slot(inputotp.SlotProps{
22 | Index: 3,
23 | Placeholder: "•",
24 | })
25 | @inputotp.Slot(inputotp.SlotProps{
26 | Index: 4,
27 | Placeholder: "•",
28 | })
29 | @inputotp.Slot(inputotp.SlotProps{
30 | Index: 5,
31 | Placeholder: "•",
32 | })
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_otp_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/inputotp"
4 | import "github.com/axzilla/templui/internal/components/label"
5 |
6 | templ InputOTPWithLabel() {
7 |
8 | @label.Label(label.Props{
9 | For: "otp-with-label",
10 | }) {
11 | Verification Code
12 | }
13 | @inputotp.InputOTP(inputotp.Props{
14 | ID: "otp-with-label",
15 | Required: true,
16 | HasError: true,
17 | }) {
18 | @inputotp.Group() {
19 | @inputotp.Slot(inputotp.SlotProps{
20 | Index: 0,
21 | })
22 | @inputotp.Slot(inputotp.SlotProps{
23 | Index: 1,
24 | })
25 | @inputotp.Slot(inputotp.SlotProps{
26 | Index: 2,
27 | })
28 | @inputotp.Slot(inputotp.SlotProps{
29 | Index: 3,
30 | })
31 | @inputotp.Slot(inputotp.SlotProps{
32 | Index: 4,
33 | })
34 | @inputotp.Slot(inputotp.SlotProps{
35 | Index: 5,
36 | })
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_password.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/input"
4 |
5 | templ InputPassword() {
6 |
7 | @input.Input(input.Props{
8 | Type: input.TypePassword,
9 | Placeholder: "your password",
10 | })
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/internal/ui/showcase/input_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/input"
5 | "github.com/axzilla/templui/internal/components/label"
6 | )
7 |
8 | templ InputWithLabel() {
9 |
10 | @label.Label(label.Props{
11 | For: "email",
12 | }) {
13 | Email
14 | }
15 | @input.Input(input.Props{
16 | ID: "email",
17 | Type: input.TypeEmail,
18 | Placeholder: "Email",
19 | })
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/internal/ui/showcase/modal_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/modal"
6 | )
7 |
8 | templ ModalDefault() {
9 | @modal.Trigger(modal.TriggerProps{
10 | ModalID: "default-modal",
11 | }) {
12 | @button.Button() {
13 | Open Modal
14 | }
15 | }
16 | @modal.Modal(modal.Props{
17 | ID: "default-modal",
18 | Class: "max-w-md",
19 | }) {
20 | @modal.Header() {
21 | Are you absolutely sure?
22 | }
23 | @modal.Body() {
24 | This action cannot be undone. This will permanently delete your account and remove your data from our servers.
25 | }
26 | @modal.Footer() {
27 |
28 | @modal.Close(modal.CloseProps{
29 | ModalID: "default-modal",
30 | }) {
31 | @button.Button() {
32 | Cancel
33 | }
34 | }
35 | @modal.Close(modal.CloseProps{
36 | ModalID: "default-modal",
37 | }) {
38 | @button.Button(button.Props{
39 | Variant: button.VariantSecondary,
40 | }) {
41 | Continue
42 | }
43 | }
44 |
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/internal/ui/showcase/pagination_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/pagination"
4 |
5 | templ PaginationDefault() {
6 | @pagination.Pagination(pagination.Props{
7 | Class: "mt-8",
8 | }) {
9 | @pagination.Content() {
10 | @pagination.Item() {
11 | @pagination.Previous(pagination.PreviousProps{
12 | Href: "?page=1",
13 | Disabled: false,
14 | Label: "Previous",
15 | })
16 | }
17 | @pagination.Item() {
18 | @pagination.Link(pagination.LinkProps{
19 | Href: "?page=1",
20 | }) {
21 | 1
22 | }
23 | }
24 | @pagination.Item() {
25 | @pagination.Link(pagination.LinkProps{
26 | Href: "?page=2",
27 | IsActive: true,
28 | }) {
29 | 2
30 | }
31 | }
32 | @pagination.Item() {
33 | @pagination.Link(pagination.LinkProps{
34 | Href: "?page=3",
35 | }) {
36 | 3
37 | }
38 | }
39 | @pagination.Item() {
40 | @pagination.Ellipsis()
41 | }
42 | @pagination.Item() {
43 | @pagination.Next(pagination.NextProps{
44 | Href: "?page=3",
45 | Label: "Next",
46 | })
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/internal/ui/showcase/pagination_with_helper.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "fmt"
5 | "github.com/axzilla/templui/internal/components/pagination"
6 | )
7 |
8 | templ PaginationWithHelper() {
9 | {{ p := pagination.CreatePagination(5, 20, 3) }}
10 | @pagination.Pagination() {
11 | @pagination.Content() {
12 | @pagination.Item() {
13 | @pagination.Previous(pagination.PreviousProps{
14 | Href: fmt.Sprintf("?page=%d", p.CurrentPage-1),
15 | Disabled: !p.HasPrevious,
16 | Label: "Previous",
17 | })
18 | }
19 | // First page with ellipsis if needed
20 | if p.Pages[0] > 1 {
21 | @pagination.Item() {
22 | @pagination.Link(pagination.LinkProps{
23 | Href: "?page=1",
24 | }) {
25 | 1
26 | }
27 | }
28 | if p.Pages[0] > 2 {
29 | @pagination.Item() {
30 | @pagination.Ellipsis()
31 | }
32 | }
33 | }
34 | // Visible pages
35 | for _, page := range p.Pages {
36 | @pagination.Item() {
37 | @pagination.Link(pagination.LinkProps{
38 | Href: fmt.Sprintf("?page=%d", page),
39 | IsActive: page == p.CurrentPage,
40 | }) {
41 | { fmt.Sprint(page) }
42 | }
43 | }
44 | }
45 | // Last page with ellipsis if needed
46 | if p.Pages[len(p.Pages)-1] < p.TotalPages {
47 | if p.Pages[len(p.Pages)-1] < p.TotalPages-1 {
48 | @pagination.Item() {
49 | @pagination.Ellipsis()
50 | }
51 | }
52 | @pagination.Item() {
53 | @pagination.Link(pagination.LinkProps{
54 | Href: fmt.Sprintf("?page=%d", p.TotalPages),
55 | }) {
56 | { fmt.Sprint(p.TotalPages) }
57 | }
58 | }
59 | }
60 | @pagination.Item() {
61 | @pagination.Next(pagination.NextProps{
62 | Href: fmt.Sprintf("?page=%d", p.CurrentPage+1),
63 | Disabled: !p.HasNext,
64 | Label: "Next",
65 | })
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/internal/ui/showcase/popover_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/input"
6 | "github.com/axzilla/templui/internal/components/label"
7 | "github.com/axzilla/templui/internal/components/popover"
8 | "github.com/axzilla/templui/internal/utils"
9 | )
10 |
11 | templ PopoverDefault() {
12 | @popover.Popover() {
13 | @popover.Trigger(popover.TriggerProps{
14 | For: "default-popover",
15 | }) {
16 | @button.Button(button.Props{
17 | Variant: button.VariantOutline,
18 | }) {
19 | Open Popover
20 | }
21 | }
22 | @popover.Content(popover.ContentProps{
23 | ID: "default-popover",
24 | }) {
25 | @PopoverContent()
26 | }
27 | }
28 | }
29 |
30 | templ PopoverContent() {
31 | {{ var id = utils.RandomID() }}
32 |
33 |
34 |
Dimensions
35 |
Set the dimensions for the layer.
36 |
37 |
38 |
39 | @label.Label(label.Props{
40 | For: "width" + id,
41 | Class: "w-24",
42 | }) {
43 | Width
44 | }
45 | @input.Input(input.Props{
46 | ID: "width" + id,
47 | Placeholder: "Width",
48 | Value: "100%",
49 | Class: "flex-1",
50 | })
51 |
52 |
53 | @label.Label(label.Props{
54 | For: "height" + id,
55 | Class: "w-24",
56 | }) {
57 | Height
58 | }
59 | @input.Input(input.Props{
60 | ID: "height" + id,
61 | Placeholder: "Height",
62 | Value: "100%",
63 | Class: "flex-1",
64 | })
65 |
66 |
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/internal/ui/showcase/popover_triggers.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/popover"
6 | )
7 |
8 | templ PopoverTriggers() {
9 |
10 | @popover.Popover() {
11 | @popover.Trigger(popover.TriggerProps{
12 | For: "hover-popover",
13 | TriggerType: popover.TriggerTypeHover,
14 | }) {
15 | @button.Button(button.Props{Variant: button.VariantOutline}) {
16 | Hover
17 | }
18 | }
19 | @popover.Content(popover.ContentProps{
20 | ID: "hover-popover",
21 | HoverDelay: 300,
22 | HoverOutDelay: 500,
23 | }) {
24 | @PopoverContent()
25 | }
26 | }
27 | @popover.Popover() {
28 | @popover.Trigger(popover.TriggerProps{
29 | For: "click-popover",
30 | }) {
31 | @button.Button(button.Props{Variant: button.VariantOutline}) {
32 | Click
33 | }
34 | }
35 | @popover.Content(popover.ContentProps{
36 | ID: "click-popover",
37 | }) {
38 | @PopoverContent()
39 | }
40 | }
41 | @popover.Popover() {
42 | @popover.Trigger(popover.TriggerProps{
43 | For: "no-clickaway-popover",
44 | }) {
45 | @button.Button(button.Props{Variant: button.VariantOutline}) {
46 | No ClickAway
47 | }
48 | }
49 | @popover.Content(popover.ContentProps{
50 | ID: "no-clickaway-popover",
51 | DisableClickAway: true,
52 | }) {
53 | @PopoverContent()
54 | }
55 | }
56 | @popover.Popover() {
57 | @popover.Trigger(popover.TriggerProps{
58 | For: "no-clickaway-esc",
59 | }) {
60 | @button.Button(button.Props{Variant: button.VariantOutline}) {
61 | No ClickAway-ESC
62 | }
63 | }
64 | @popover.Content(popover.ContentProps{
65 | ID: "no-clickaway-esc",
66 | DisableClickAway: true,
67 | DisableESC: true,
68 | }) {
69 | @PopoverContent()
70 | }
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/internal/ui/showcase/progress_colors.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/progress"
4 |
5 | templ ProgressColors() {
6 |
7 | @progress.Progress(progress.Props{
8 | Value: 50,
9 | Variant: progress.VariantSuccess,
10 | })
11 | @progress.Progress(progress.Props{
12 | Value: 75,
13 | Variant: progress.VariantDanger,
14 | })
15 | @progress.Progress(progress.Props{
16 | Value: 90,
17 | Variant: progress.VariantWarning,
18 | })
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/progress_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/progress"
4 |
5 | templ ProgressDefault() {
6 |
7 | @progress.Progress(progress.Props{
8 | Value: 25,
9 | })
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/progress_sizes.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/progress"
4 |
5 | templ ProgressSizes() {
6 |
7 | @progress.Progress(progress.Props{
8 | Value: 50,
9 | Size: progress.SizeSm,
10 | })
11 | @progress.Progress(progress.Props{
12 | Value: 65,
13 | Size: progress.SizeLg,
14 | })
15 | @progress.Progress(progress.Props{
16 | Value: 80,
17 | Size: progress.SizeLg,
18 | })
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/radio_card_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/icon"
5 | "github.com/axzilla/templui/internal/components/radiocard"
6 | )
7 |
8 | templ RadioCardDefault() {
9 |
10 | @radiocard.RadioCard(radiocard.Props{
11 | ID: "comp-plan-basic",
12 | Name: "comp-plan",
13 | Value: "basic",
14 | }) {
15 | @radiocard.Header() {
16 |
17 | @icon.Package(icon.Props{Size: 20})
18 |
Basic Plan
19 |
20 | }
21 | @radiocard.Description() {
22 | Essential features for individuals and small teams
23 | }
24 | @radiocard.Footer() {
25 | @radioCardPriceFooter("$5.99")
26 | }
27 | }
28 | @radiocard.RadioCard(radiocard.Props{
29 | ID: "comp-plan-pro",
30 | Name: "comp-plan",
31 | Value: "pro",
32 | }) {
33 | @radiocard.Header() {
34 |
35 | @icon.Star(icon.Props{Size: 20})
36 |
Pro Plan
37 |
38 | }
39 | @radiocard.Description() {
40 | Enhanced capabilities for growing businesses.
41 | }
42 | @radiocard.Footer() {
43 | @radioCardPriceFooter("$14.99")
44 | }
45 | }
46 | @radiocard.RadioCard(radiocard.Props{
47 | ID: "comp-plan-enterprise",
48 | Name: "comp-plan",
49 | Value: "enterprise",
50 | Disabled: true,
51 | }) {
52 | @radiocard.Header() {
53 |
54 | @icon.Building(icon.Props{Size: 20})
55 |
Enterprise Plan
56 |
57 | }
58 | @radiocard.Description() {
59 | Advanced features for large organizations
60 | }
61 | @radiocard.Footer() {
62 | @radioCardPriceFooter("$29.99")
63 | }
64 | }
65 |
66 | }
67 |
68 | templ radioCardPriceFooter(price string) {
69 |
70 | Price
71 | { price }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/internal/ui/showcase/radio_checked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/radio"
4 |
5 | templ RadioChecked() {
6 | @radio.Radio(radio.Props{
7 | Checked: true,
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/radio_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/radio"
4 |
5 | templ RadioDefault() {
6 | @radio.Radio()
7 | }
8 |
--------------------------------------------------------------------------------
/internal/ui/showcase/radio_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/radio"
6 | )
7 |
8 | templ RadioDisabled() {
9 |
10 | @radio.Radio(radio.Props{
11 | ID: "radio-disabled",
12 | Disabled: true,
13 | })
14 | @label.Label(label.Props{
15 | For: "radio-disabled",
16 | }) {
17 | Disabled
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/radio_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/radio"
6 | )
7 |
8 | templ RadioForm() {
9 |
10 | @form.Item() {
11 | @form.ItemFlex() {
12 | @radio.Radio(radio.Props{
13 | Name: "radio-form",
14 | ID: "r1",
15 | Checked: true,
16 | })
17 | @form.Label(form.LabelProps{
18 | For: "r1",
19 | }) {
20 | All new products
21 | }
22 | }
23 | @form.ItemFlex() {
24 | @radio.Radio(radio.Props{
25 | Name: "radio-form",
26 | ID: "r2",
27 | Disabled: true,
28 | })
29 | @form.Label(form.LabelProps{
30 | For: "r2",
31 | }) {
32 | Create a wishlist (Coming Soon)
33 | }
34 | }
35 | @form.ItemFlex() {
36 | @radio.Radio(radio.Props{
37 | Name: "radio-form",
38 | ID: "r3",
39 | })
40 | @form.Label(form.LabelProps{
41 | For: "r3",
42 | }) {
43 | No notifications
44 | }
45 | }
46 | @form.Description() {
47 | You can change your preferences at any time.
48 | }
49 | @form.Message(form.MessageProps{
50 | Variant: form.MessageVariantError,
51 | }) {
52 | We will send you an email when new products are available.
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/internal/ui/showcase/radio_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/radio"
6 | )
7 |
8 | templ RadioWithLabel() {
9 |
10 | @radio.Radio(radio.Props{
11 | ID: "radio-with-label",
12 | })
13 | @label.Label(label.Props{
14 | For: "radio-with-label",
15 | }) {
16 | Label
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/internal/ui/showcase/rating_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/rating"
4 |
5 | templ RatingDefault() {
6 | @rating.Rating(rating.Props{
7 | Value: 3.5,
8 | ReadOnly: false,
9 | Precision: 0.5,
10 | }) {
11 | @rating.Group() {
12 | for i := 1; i <= 5; i++ {
13 | @rating.Item(rating.ItemProps{
14 | Value: i,
15 | Style: rating.StyleStar,
16 | })
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/rating_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/rating"
6 | )
7 |
8 | templ RatingForm() {
9 |
41 | }
42 |
--------------------------------------------------------------------------------
/internal/ui/showcase/rating_max_values.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/rating"
4 |
5 | templ RatingMaxValues() {
6 | @rating.Rating(rating.Props{
7 | Value: 7,
8 | }) {
9 | @rating.Group() {
10 | for i := 1; i <= 10; i++ {
11 | @rating.Item(rating.ItemProps{
12 | Value: i,
13 | Style: rating.StyleStar,
14 | Class: "scale-75", // Smaller stars for better display
15 | })
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/internal/ui/showcase/rating_precision.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/rating"
4 |
5 | templ RatingPrecision() {
6 | @rating.Rating(rating.Props{
7 | Value: 1.3,
8 | ReadOnly: true,
9 | Precision: 0.5,
10 | }) {
11 | @rating.Group() {
12 | for i := 1; i <= 5; i++ {
13 | @rating.Item(rating.ItemProps{
14 | Value: i,
15 | Style: rating.StyleStar,
16 | })
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/rating_styles.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/rating"
4 |
5 | templ RatingStyles() {
6 |
7 | @rating.Rating(rating.Props{
8 | Value: 2,
9 | }) {
10 | @rating.Group() {
11 | for i := 1; i <= 5; i++ {
12 | @rating.Item(rating.ItemProps{
13 | Value: i,
14 | Style: rating.StyleStar,
15 | })
16 | }
17 | }
18 | }
19 | @rating.Rating(rating.Props{
20 | Value: 3,
21 | }) {
22 | @rating.Group() {
23 | for i := 1; i <= 5; i++ {
24 | @rating.Item(rating.ItemProps{
25 | Value: i,
26 | Style: rating.StyleHeart,
27 | })
28 | }
29 | }
30 | }
31 | @rating.Rating(rating.Props{
32 | Value: 4,
33 | }) {
34 | @rating.Group() {
35 | for i := 1; i <= 5; i++ {
36 | @rating.Item(rating.ItemProps{
37 | Value: i,
38 | Style: rating.StyleEmoji,
39 | })
40 | }
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/internal/ui/showcase/rating_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/rating"
6 | )
7 |
8 | templ RatingWithLabel() {
9 |
10 |
11 | @label.Label(label.Props{
12 | For: "rating-with-label",
13 | }) {
14 | Fruit
15 | }
16 | @rating.Rating(rating.Props{
17 | Value: 2,
18 | }) {
19 | @rating.Group() {
20 | for i := 1; i <= 5; i++ {
21 | @rating.Item(rating.ItemProps{
22 | Value: i,
23 | Style: rating.StyleStar,
24 | })
25 | }
26 | }
27 | }
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/internal/ui/showcase/select_box_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/selectbox"
4 |
5 | templ SelectBoxDefault() {
6 |
7 | @selectbox.SelectBox() {
8 | @selectbox.Trigger() {
9 | @selectbox.Value(selectbox.ValueProps{
10 | Placeholder: "Select a fruit",
11 | })
12 | }
13 | @selectbox.Content() {
14 | @selectbox.Group() {
15 | @selectbox.Label() {
16 | Fruits
17 | }
18 | @selectbox.Item(selectbox.ItemProps{
19 | Value: "apple",
20 | }) {
21 | Apple
22 | }
23 | @selectbox.Item(selectbox.ItemProps{
24 | Value: "banana",
25 | }) {
26 | Banana
27 | }
28 | @selectbox.Item(selectbox.ItemProps{
29 | Value: "blueberry",
30 | }) {
31 | Blueberry
32 | }
33 | @selectbox.Item(selectbox.ItemProps{
34 | Value: "grapes",
35 | }) {
36 | Grapes
37 | }
38 | @selectbox.Item(selectbox.ItemProps{
39 | Value: "pineapple",
40 | }) {
41 | Pineapple
42 | }
43 | }
44 | }
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/internal/ui/showcase/select_box_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/selectbox"
4 |
5 | templ SelectBoxDisabled() {
6 |
7 | @selectbox.SelectBox() {
8 | @selectbox.Trigger(selectbox.TriggerProps{
9 | Disabled: true,
10 | }) {
11 | @selectbox.Value(selectbox.ValueProps{
12 | Placeholder: "Select a fruit",
13 | })
14 | }
15 | @selectbox.Content() {
16 | @selectbox.Label() {
17 | Fruits
18 | }
19 | @selectbox.Item(selectbox.ItemProps{
20 | Value: "apple",
21 | }) {
22 | Apple
23 | }
24 | @selectbox.Item(selectbox.ItemProps{
25 | Value: "banana",
26 | }) {
27 | Banana
28 | }
29 | }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/internal/ui/showcase/select_box_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/selectbox"
6 | )
7 |
8 | templ SelectBoxForm() {
9 |
10 | @form.Item() {
11 | @form.Label(form.LabelProps{
12 | For: "select-form",
13 | }) {
14 | Fruit
15 | }
16 | @selectbox.SelectBox() {
17 | @selectbox.Trigger(selectbox.TriggerProps{
18 | ID: "select-form",
19 | Name: "fruit",
20 | Required: true,
21 | HasError: true,
22 | }) {
23 | @selectbox.Value(selectbox.ValueProps{
24 | Placeholder: "Select a fruit",
25 | })
26 | }
27 | @selectbox.Content() {
28 | @selectbox.Item(selectbox.ItemProps{
29 | Value: "apple",
30 | }) {
31 | Apple
32 | }
33 | @selectbox.Item(selectbox.ItemProps{
34 | Value: "banana",
35 | }) {
36 | Banana
37 | }
38 | @selectbox.Item(selectbox.ItemProps{
39 | Value: "blueberry",
40 | Selected: true,
41 | }) {
42 | Blueberry
43 | }
44 | @selectbox.Item(selectbox.ItemProps{
45 | Value: "grapes",
46 | }) {
47 | Grapes
48 | }
49 | @selectbox.Item(selectbox.ItemProps{
50 | Value: "pineapple",
51 | Disabled: true,
52 | }) {
53 | Pineapple (out of stock)
54 | }
55 | }
56 | }
57 | @form.Description() {
58 | Select a fruit category.
59 | }
60 | @form.Message(form.MessageProps{
61 | Variant: form.MessageVariantError,
62 | }) {
63 | A fruit selection is required.
64 | }
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/internal/ui/showcase/select_box_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/selectbox"
6 | )
7 |
8 | templ SelectBoxWithLabel() {
9 |
10 | @label.Label(label.Props{
11 | For: "select-with-label",
12 | }) {
13 | Fruit
14 | }
15 | @selectbox.SelectBox() {
16 | @selectbox.Trigger(selectbox.TriggerProps{
17 | ID: "select-with-label",
18 | }) {
19 | @selectbox.Value(selectbox.ValueProps{
20 | Placeholder: "Select a fruit",
21 | })
22 | }
23 | @selectbox.Content() {
24 | @selectbox.Label() {
25 | Fruits
26 | }
27 | @selectbox.Item(selectbox.ItemProps{
28 | Value: "apple",
29 | }) {
30 | Apple
31 | }
32 | @selectbox.Item(selectbox.ItemProps{
33 | Value: "banana",
34 | }) {
35 | Banana
36 | }
37 | @selectbox.Item(selectbox.ItemProps{
38 | Value: "orange",
39 | }) {
40 | Orange
41 | }
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/internal/ui/showcase/separator_decorated.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/separator"
4 |
5 | templ SeparatorDecorated() {
6 |
7 | @separator.Separator(separator.Props{
8 | Decoration: separator.DecorationDashed,
9 | }) {
10 | DASHED
11 | }
12 | @separator.Separator(separator.Props{
13 | Decoration: separator.DecorationDotted,
14 | }) {
15 | DOTTED
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/internal/ui/showcase/separator_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/separator"
4 |
5 | templ SeparatorDefault() {
6 |
7 |
Top
8 | @separator.Separator()
9 |
Bottom
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/separator_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/separator"
4 |
5 | templ SeparatorLabel() {
6 |
7 | @separator.Separator(separator.Props{
8 | Class: "w-full",
9 | }) {
10 | OR
11 | }
12 | @separator.Separator(separator.Props{
13 | Class: "h-24",
14 | Orientation: separator.OrientationVertical,
15 | }) {
16 | OR
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/internal/ui/showcase/separator_vertical.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/separator"
4 |
5 | templ SeparatorVertical() {
6 |
7 |
8 |
Left
9 | @separator.Separator(separator.Props{
10 | Orientation: separator.OrientationVertical,
11 | Class: "mx-4",
12 | })
13 |
Right
14 |
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/internal/ui/showcase/skeleton_card.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/skeleton"
4 |
5 | templ SkeletonCard() {
6 |
7 | @skeleton.Skeleton(skeleton.Props{Class: "h-[200px] w-full rounded-md mb-4"})
8 |
9 | @skeleton.Skeleton(skeleton.Props{Class: "h-5 w-2/3"})
10 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-full"})
11 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-full"})
12 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-3/4"})
13 |
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/internal/ui/showcase/skeleton_dashboard.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/skeleton"
4 |
5 | templ SkeletonDashboard() {
6 |
7 |
8 | for i := 0; i < 3; i++ {
9 |
10 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-20 mb-2"})
11 | @skeleton.Skeleton(skeleton.Props{Class: "h-8 w-24 mb-4"})
12 |
13 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-12"})
14 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-4"})
15 |
16 |
17 | }
18 |
19 |
20 | @skeleton.Skeleton(skeleton.Props{Class: "h-5 w-1/3 mb-6"})
21 | @skeleton.Skeleton(skeleton.Props{Class: "h-[240px] w-full rounded-md"})
22 |
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/internal/ui/showcase/skeleton_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/skeleton"
4 |
5 | templ SkeletonDefault() {
6 |
7 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-full"})
8 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-2/3"})
9 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-1/3"})
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/skeleton_profile.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/skeleton"
4 |
5 | templ SkeletonProfile() {
6 |
7 | @skeleton.Skeleton(skeleton.Props{Class: "h-12 w-12 rounded-full"})
8 |
9 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-full"})
10 | @skeleton.Skeleton(skeleton.Props{Class: "h-4 w-3/4"})
11 |
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/internal/ui/showcase/slider_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/slider"
4 |
5 | templ SliderDefault() {
6 |
7 | @slider.Slider() {
8 | @slider.Input()
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/slider_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/slider"
6 | )
7 |
8 | templ SliderDisabled() {
9 |
10 | @slider.Slider() {
11 |
12 | @label.Label() {
13 | Volume
14 | }
15 | @slider.Value(slider.ValueProps{
16 | For: "slider-disabled",
17 | })
18 |
19 | @slider.Input(slider.InputProps{
20 | ID: "slider-disabled",
21 | Value: 20,
22 | Min: -20,
23 | Max: 200,
24 | Step: 20,
25 | Disabled: true,
26 | })
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/internal/ui/showcase/slider_external_value.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/slider"
4 |
5 | templ SliderExternalValue() {
6 |
7 |
8 | @slider.Slider() {
9 | @slider.Input(slider.InputProps{
10 | ID: "slider-external-value",
11 | Value: 50,
12 | Min: 0,
13 | Max: 100,
14 | Step: 1,
15 | })
16 | }
17 |
18 |
19 |
External value (linked to the slider):
20 |
21 | @slider.Value(slider.ValueProps{
22 | For: "slider-external-value",
23 | Class: "text-3xl font-bold text-primary",
24 | })
25 | %
26 |
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/internal/ui/showcase/slider_steps.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/slider"
6 | )
7 |
8 | templ SliderSteps() {
9 |
10 | @slider.Slider(slider.Props{}) {
11 |
12 | @label.Label() {
13 | Zoom Level
14 | }
15 |
16 | @slider.Value(slider.ValueProps{
17 | For: "slider-steps",
18 | })
19 |
20 |
21 | @slider.Input(slider.InputProps{
22 | ID: "slider-steps",
23 | Name: "slider-steps",
24 | Value: 100,
25 | Min: 0,
26 | Max: 200,
27 | Step: 25,
28 | })
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/internal/ui/showcase/slider_value.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/slider"
4 |
5 | templ SliderValue() {
6 |
7 | @slider.Slider() {
8 |
9 | @slider.Value(slider.ValueProps{
10 | For: "slider-value",
11 | })
12 |
13 | @slider.Input(slider.InputProps{
14 | ID: "slider-value",
15 | Value: 75,
16 | Min: 0,
17 | Max: 100,
18 | Step: 1,
19 | })
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/internal/ui/showcase/spinner_colors.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/spinner"
4 |
5 | templ SpinnerColors() {
6 |
7 | @spinner.Spinner(spinner.Props{
8 | Size: spinner.SizeMd,
9 | Color: "text-red-500",
10 | })
11 | @spinner.Spinner(spinner.Props{
12 | Size: spinner.SizeMd,
13 | Color: "text-green-500",
14 | })
15 | @spinner.Spinner(spinner.Props{
16 | Size: spinner.SizeMd,
17 | Color: "text-blue-500",
18 | })
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/spinner_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/spinner"
4 |
5 | templ SpinnerDefault() {
6 | @spinner.Spinner(spinner.Props{
7 | Size: spinner.SizeMd,
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/spinner_in_button.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/spinner"
6 | )
7 |
8 | templ SpinnerInButton() {
9 | @button.Button(button.Props{
10 | Attributes: templ.Attributes{
11 | "disabled": "true",
12 | },
13 | }) {
14 |
15 | @spinner.Spinner(spinner.Props{
16 | Size: spinner.SizeSm,
17 | Color: "text-primary-foreground",
18 | })
19 | Loading
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/internal/ui/showcase/spinner_sizes.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/spinner"
4 |
5 | templ SpinnerSizes() {
6 |
7 | @spinner.Spinner(spinner.Props{
8 | Size: spinner.SizeSm,
9 | })
10 | @spinner.Spinner(spinner.Props{
11 | Size: spinner.SizeMd,
12 | })
13 | @spinner.Spinner(spinner.Props{
14 | Size: spinner.SizeLg,
15 | })
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/internal/ui/showcase/table.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/table"
4 |
5 | templ Table() {
6 | @table.Table() {
7 | @table.Caption() {
8 | A list of your recent hires.
9 | }
10 | @table.Header() {
11 | @table.Row() {
12 | @table.Head() {
13 | Name
14 | }
15 | @table.Head() {
16 | Role
17 | }
18 | @table.Head() {
19 | Status
20 | }
21 | @table.Head() {
22 | Actions
23 | }
24 | }
25 | }
26 | @table.Body() {
27 | @table.Row() {
28 | @table.Cell() {
29 | John Doe
30 | }
31 | @table.Cell() {
32 | Software Engineer
33 | }
34 | @table.Cell() {
35 | Active
36 | }
37 | @table.Cell() {
38 | Edit
39 | }
40 | }
41 | @table.Row() {
42 | @table.Cell() {
43 | Jane Smith
44 | }
45 | @table.Cell() {
46 | Designer
47 | }
48 | @table.Cell() {
49 | Active
50 | }
51 | @table.Cell() {
52 | Edit
53 | }
54 | }
55 | @table.Row() {
56 | @table.Cell() {
57 | Bob Johnson
58 | }
59 | @table.Cell() {
60 | Product Manager
61 | }
62 | @table.Cell() {
63 | Inactive
64 | }
65 | @table.Cell() {
66 | Edit
67 | }
68 | }
69 | @table.Footer() {
70 | @table.Row() {
71 | @table.Head() {
72 | 3 items
73 | }
74 | @table.Head() {
75 | 1 page
76 | }
77 | @table.Head() {
78 | 1-3 of 3
79 | }
80 | @table.Head() {
81 | Next
82 | }
83 | }
84 | }
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/internal/ui/showcase/textarea_auto_resize.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/textarea"
4 |
5 | templ TextareaAutoResize() {
6 |
7 | @textarea.Textarea(textarea.Props{
8 | Placeholder: "Start typing to see the magic...",
9 | AutoResize: true,
10 | })
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/internal/ui/showcase/textarea_custom_rows.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/textarea"
4 |
5 | templ TextareaCustomRows() {
6 |
7 | @textarea.Textarea(textarea.Props{
8 | Placeholder: "Type your message here...",
9 | Rows: 6,
10 | })
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/internal/ui/showcase/textarea_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/textarea"
4 |
5 | templ TextareaDefault() {
6 |
7 | @textarea.Textarea(textarea.Props{
8 | Placeholder: "Type your message here...",
9 | })
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/textarea_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/textarea"
6 | )
7 |
8 | templ TextareaDisabled() {
9 |
10 | @label.Label(label.Props{
11 | For: "textarea-disabled",
12 | }) {
13 | Your Message
14 | }
15 | @textarea.Textarea(textarea.Props{
16 | ID: "textarea-disabled",
17 | Disabled: true,
18 | Placeholder: "Type your message here...",
19 | })
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/internal/ui/showcase/textarea_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/textarea"
6 | )
7 |
8 | templ TextareaForm() {
9 |
10 | @form.Item() {
11 | @form.Label(form.LabelProps{
12 | For: "textarea-form",
13 | }) {
14 | Your Message
15 | }
16 | @textarea.Textarea(textarea.Props{
17 | ID: "textarea-form",
18 | Name: "message",
19 | Placeholder: "Type your message here...",
20 | })
21 | @form.Description() {
22 | Please type your message in the textarea.
23 | }
24 | @form.Message(form.MessageProps{
25 | Variant: form.MessageVariantError,
26 | }) {
27 | This message is required.
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/internal/ui/showcase/textarea_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/textarea"
6 | )
7 |
8 | templ TextareaWithLabel() {
9 |
10 | @label.Label(label.Props{
11 | For: "textarea-with-label",
12 | }) {
13 | Your Message
14 | }
15 | @textarea.Textarea(textarea.Props{
16 | ID: "textarea-with-label",
17 | Placeholder: "Type your message here...",
18 | Rows: 4,
19 | })
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/internal/ui/showcase/time_picker_12hour.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/timepicker"
4 |
5 | templ TimePicker12Hour() {
6 |
7 | @timepicker.TimePicker(timepicker.Props{
8 | Use12Hours: true,
9 | })
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/time_picker_custom_placeholder.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/timepicker"
4 |
5 | templ TimePickerCustomPlaceholder() {
6 |
7 | @timepicker.TimePicker(timepicker.Props{
8 | Placeholder: "When do you want to meet?",
9 | })
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/internal/ui/showcase/time_picker_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/timepicker"
4 |
5 | templ TimePickerDefault() {
6 |
7 | @timepicker.TimePicker()
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/time_picker_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/timepicker"
6 | )
7 |
8 | templ TimePickerForm() {
9 |
10 | @form.Item() {
11 | @form.Label(form.LabelProps{
12 | For: "time-picker-form",
13 | }) {
14 | Select a time
15 | }
16 | @timepicker.TimePicker(timepicker.Props{
17 | ID: "time-picker-form",
18 | Name: "time-picker-form",
19 | HasError: true,
20 | })
21 | @form.Description() {
22 | Select a time from the dropdown.
23 | }
24 | @form.Message(form.MessageProps{
25 | Variant: form.MessageVariantError,
26 | }) {
27 | Please select a time
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/internal/ui/showcase/time_picker_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/timepicker"
6 | )
7 |
8 | templ TimePickerLabel() {
9 |
10 | @label.Label(label.Props{
11 | For: "time-picker-label",
12 | }) {
13 | Select a time
14 | }
15 | @timepicker.TimePicker(timepicker.Props{
16 | ID: "time-picker-label",
17 | Use12Hours: true,
18 | })
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/time_picker_selected_time.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/timepicker"
5 | "time"
6 | )
7 |
8 | templ TimePickerSelectedTime() {
9 |
10 | @timepicker.TimePicker(timepicker.Props{
11 | Value: time.Now(),
12 | })
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/internal/ui/showcase/toast_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/button"
4 |
5 | templ ToastDefault() {
6 |
26 | }
27 |
--------------------------------------------------------------------------------
/internal/ui/showcase/toggle_checked.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/toggle"
4 |
5 | templ ToggleChecked() {
6 | @toggle.Toggle(toggle.Props{
7 | Checked: true,
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/internal/ui/showcase/toggle_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import "github.com/axzilla/templui/internal/components/toggle"
4 |
5 | templ ToggleDefault() {
6 | @toggle.Toggle()
7 | }
8 |
--------------------------------------------------------------------------------
/internal/ui/showcase/toggle_disabled.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/toggle"
6 | )
7 |
8 | templ ToggleDisabled() {
9 |
10 | @toggle.Toggle(toggle.Props{
11 | ID: "toggle-disabled",
12 | Disabled: true,
13 | })
14 | @label.Label(label.Props{
15 | For: "toggle-disabled",
16 | }) {
17 | Airplane Mode
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/internal/ui/showcase/toggle_form.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/form"
5 | "github.com/axzilla/templui/internal/components/toggle"
6 | )
7 |
8 | templ ToggleForm() {
9 |
10 | @form.Item() {
11 | @form.ItemFlex() {
12 | @toggle.Toggle(toggle.Props{
13 | ID: "airplane-mode",
14 | Name: "airplane",
15 | })
16 | @form.Label(form.LabelProps{
17 | For: "airplane-mode",
18 | }) {
19 | Airplane Mode
20 | }
21 | }
22 | @form.ItemFlex() {
23 | @toggle.Toggle(toggle.Props{
24 | ID: "wifi-mode",
25 | Name: "wifi",
26 | Disabled: true,
27 | })
28 | @form.Label(form.LabelProps{
29 | For: "wifi-mode",
30 | }) {
31 | Wi-Fi
32 | }
33 | }
34 | @form.ItemFlex() {
35 | @toggle.Toggle(toggle.Props{
36 | ID: "bluetooth-mode",
37 | Name: "bluetooth",
38 | Checked: true,
39 | })
40 | @form.Label(form.LabelProps{
41 | For: "bluetooth-mode",
42 | }) {
43 | Bluetooth
44 | }
45 | }
46 | @form.Description() {
47 | Manage your devices connectivity options.
48 | }
49 | @form.Message(form.MessageProps{
50 | Variant: form.MessageVariantError,
51 | }) {
52 | Please configure your connectivity settings.
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/internal/ui/showcase/toggle_with_label.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/label"
5 | "github.com/axzilla/templui/internal/components/toggle"
6 | )
7 |
8 | templ ToggleWithLabel() {
9 |
10 | @toggle.Toggle(toggle.Props{
11 | ID: "toggle-with-label",
12 | })
13 | @label.Label(label.Props{
14 | For: "toggle-with-label",
15 | }) {
16 | Airplane Mode
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/internal/ui/showcase/tooltip_default.templ:
--------------------------------------------------------------------------------
1 | package showcase
2 |
3 | import (
4 | "github.com/axzilla/templui/internal/components/button"
5 | "github.com/axzilla/templui/internal/components/tooltip"
6 | )
7 |
8 | templ TooltipDefault() {
9 | @tooltip.Tooltip() {
10 | @tooltip.Trigger(tooltip.TriggerProps{
11 | For: "tooltip-default",
12 | }) {
13 | @button.Button(button.Props{
14 | Variant: button.VariantOutline,
15 | }) {
16 | Hover Me
17 | }
18 | }
19 | @tooltip.Content(tooltip.ContentProps{
20 | ID: "tooltip-default",
21 | Position: tooltip.PositionTop,
22 | HoverDelay: 500,
23 | HoverOutDelay: 100,
24 | }) {
25 | Add to cart
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internal/utils/internal.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/base64"
5 | "fmt"
6 |
7 | "math/rand"
8 | )
9 |
10 | func GenerateNonce() (string, error) {
11 | nonceBytes := make([]byte, 16)
12 | _, err := rand.Read(nonceBytes)
13 | if err != nil {
14 | return "", fmt.Errorf("failed to generate nonce: %w", err)
15 | }
16 | return base64.StdEncoding.EncodeToString(nonceBytes), nil
17 | }
18 |
--------------------------------------------------------------------------------
/internal/utils/templui.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 |
6 | "crypto/rand"
7 |
8 | "github.com/a-h/templ"
9 |
10 | twmerge "github.com/Oudwins/tailwind-merge-go"
11 | )
12 |
13 | // TwMerge combines Tailwind classes and resolves conflicts.
14 | // Example: "bg-red-500 hover:bg-blue-500", "bg-green-500" → "hover:bg-blue-500 bg-green-500"
15 | func TwMerge(classes ...string) string {
16 | return twmerge.Merge(classes...)
17 | }
18 |
19 | // TwIf returns value if condition is true, otherwise an empty value of type T.
20 | // Example: true, "bg-red-500" → "bg-red-500"
21 | func If[T comparable](condition bool, value T) T {
22 | var empty T
23 | if condition {
24 | return value
25 | }
26 | return empty
27 | }
28 |
29 | // TwIfElse returns trueValue if condition is true, otherwise falseValue.
30 | // Example: true, "bg-red-500", "bg-gray-300" → "bg-red-500"
31 | func IfElse[T any](condition bool, trueValue T, falseValue T) T {
32 | if condition {
33 | return trueValue
34 | }
35 | return falseValue
36 | }
37 |
38 | // MergeAttributes combines multiple Attributes into one.
39 | // Example: MergeAttributes(attr1, attr2) → combined attributes
40 | func MergeAttributes(attrs ...templ.Attributes) templ.Attributes {
41 | merged := templ.Attributes{}
42 | for _, attr := range attrs {
43 | for k, v := range attr {
44 | merged[k] = v
45 | }
46 | }
47 | return merged
48 | }
49 |
50 | // RandomID generates a random ID string.
51 | // Example: RandomID() → "id-1a2b3c"
52 | func RandomID() string {
53 | return fmt.Sprintf("id-%s", rand.Text())
54 | }
55 |
--------------------------------------------------------------------------------
/middleware/csp.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "strings"
8 |
9 | "github.com/a-h/templ"
10 | "github.com/axzilla/templui/internal/utils"
11 | )
12 |
13 | type CSPConfig struct {
14 | ScriptSrc []string // External script domains allowed
15 | }
16 |
17 | func WithCSP(config CSPConfig) func(http.Handler) http.Handler {
18 | return func(next http.Handler) http.Handler {
19 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20 | nonce, err := utils.GenerateNonce()
21 | if err != nil {
22 | log.Printf("failed to generate nonce: %v", err)
23 | w.Header().Set("Content-Security-Policy", "script-src 'self'")
24 | http.Error(w, "Internal Server Error", http.StatusInternalServerError)
25 | return
26 | }
27 |
28 | // Combine all script sources
29 | scriptSources := append(
30 | []string{"'self'", fmt.Sprintf("'nonce-%s'", nonce)},
31 | config.ScriptSrc...)
32 |
33 | csp := fmt.Sprintf("script-src %s", strings.Join(scriptSources, " "))
34 | w.Header().Set("Content-Security-Policy", csp)
35 |
36 | next.ServeHTTP(w, r.WithContext(templ.WithNonce(r.Context(), nonce)))
37 | })
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "@html-eslint/eslint-plugin": "^0.37.0",
4 | "@html-eslint/parser": "^0.37.0",
5 | "eslint": "^9.24.0",
6 | "eslint-plugin-html-tailwind": "^1.2.1",
7 | "tailwindcss": "^4.1.3"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/shiki/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use an official Node.js runtime as a parent image (Alpine for smaller size)
2 | FROM node:20-alpine
3 |
4 | # Set the working directory in the container
5 | WORKDIR /shiki-app
6 |
7 | # Copy package.json and package-lock.json (or yarn.lock)
8 | # This step leverages Docker layer caching
9 | COPY package*.json ./
10 |
11 | # Install pm2 globally and then production dependencies
12 | RUN npm install -g pm2 && npm install --production
13 |
14 | # Copy the rest of the application code
15 | COPY . .
16 |
17 | # Make port 3000 available within the Docker network (not exposed to host by default)
18 | EXPOSE 3000
19 |
20 | # Define the command to run the application using pm2
21 | # -i max: Start instances based on available vCPUs
22 | # --max-memory-restart: Restart instance if it exceeds this memory limit
23 | CMD ["pm2-runtime", "server.js", "-i", "max", "--max-memory-restart", "512M"]
--------------------------------------------------------------------------------
/shiki/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shiki",
3 | "version": "1.0.0",
4 | "description": "Simple service to highlight code using Shiki",
5 | "main": "server.js",
6 | "type": "module",
7 | "scripts": {
8 | "start": "nodemon server.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "express": "^4.19.2",
15 | "shiki": "^1.10.3",
16 | "nodemon": "^3.1.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /assets/img/social-preview.png
3 | Allow: /
4 | Disallow: /assets/
5 |
6 | Sitemap: https://templui.io/sitemap.xml
7 |
--------------------------------------------------------------------------------
/static/static.go:
--------------------------------------------------------------------------------
1 | package static
2 |
3 | import "embed"
4 |
5 | //go:embed *
6 | var Files embed.FS
7 |
--------------------------------------------------------------------------------