├── .gitignore
├── Makefile
├── README.MD
├── components
├── App.templ
├── App_templ.go
├── Contact.templ
├── Contact_templ.go
├── Examples.templ
├── Examples_templ.go
├── Home.templ
└── Home_templ.go
├── input.css
├── main.go
├── package.json
├── public
├── contact.html
├── examples.html
├── index.html
└── static
│ ├── assets
│ └── logo.png
│ ├── css
│ └── styles.css
│ └── js
│ └── alpine.min.js
├── tailwind.config.js
└── thumb.png
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | bun.lockb
4 |
5 | # macOS
6 | .DS_Store
7 |
8 | # Windows
9 | Thumbs.db
10 |
11 | # Android
12 | *.lock
13 |
14 |
15 | go.sum
16 | go.mod
17 | public/static/assets/demo.mov
18 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Original commands
2 | run:
3 | go run main.go
4 |
5 | compile: tailwindcss templ run
6 |
7 | vite:
8 | bun run vite ./public
9 |
10 | watch:
11 | find . -name "*.templ" | entr -r make compile
12 |
13 | test:
14 | go test -v ./... -count=1
15 |
16 | tailwindcss:
17 | bun run tailwindcss --config tailwind.config.js -i input.css -o ./public/static/css/styles.css
18 |
19 | templ:
20 | ~/go/bin/templ generate ./components
21 |
22 | .PHONY: run compile vite watch test tailwindcss templ
23 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # GOAT
2 |
3 | 
4 |
5 | This project is a starting point for developing web apps using [a-h/templ](https://github.com/a-h/templ).
6 |
7 | ## Tech Stack
8 | - [Go](https://golang.org/) (with [Templ](https://github.com/a-h/templ) for logic behind templating)
9 | - [Alpine.js](https://alpinejs.dev/) (JavaScript framework)
10 | - [Tailwind CSS](https://tailwindcss.com/) (CSS framework)
11 | - [Vite](https://vitejs.dev/) (optional, for hot reloading)
12 |
13 | ## Dependencies
14 | - Alpine.js (as a file under /public/static/js/alpine.min.js)
15 | - [Bun](https://bun.sh/) (or [Yarn](https://yarnpkg.com/) or [pnpm](https://pnpm.io/)) used to install/run Vite and Tailwind CSS
16 | - Go
17 | - Templ CLI (install using `go install github.com/a-h/templ/cmd/templ@latest`)
18 | - [Make](https://www.gnu.org/software/make/)
19 | - [entr](https://github.com/eradman/entr)
20 |
21 | ## Usage
22 | 1. Clone the repo:
23 | ```
24 | git clone https://github.com/morethancoder/goat.git
25 | ```
26 |
27 | 2. Initialize the project:
28 | ```
29 | go mod init yourprojectname && go mod tidy
30 | ```
31 |
32 | 3. Change the import in main.go:
33 | ```go
34 | // Change this:
35 | "morethancoder/goat/components"
36 | // To:
37 | "yourprojectname/components"
38 | ```
39 |
40 | 4. Install required Node modules:
41 | ```
42 | bun install
43 | ```
44 |
45 | 5. Start the Vite dev server:
46 | ```
47 | make vite
48 | ```
49 |
50 | 6. Start file watching & compiling:
51 | ```
52 | make watch
53 | ```
54 |
55 | 7. Star the repo!
56 |
--------------------------------------------------------------------------------
/components/App.templ:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | templ App(route string) {
4 |
5 |
6 |
7 |
8 |
9 |
10 | GOAT Stack
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
32 |
33 | @Header()
34 | switch route {
35 | case "/":
36 | @Home()
37 | case "/examples":
38 | @Examples()
39 | case "/contact":
40 | @Contact()
41 | default:
42 | @Home()
43 | }
44 | @Footer()
45 |
46 |
47 |
48 | }
49 |
50 | templ Header() {
51 |
79 | }
80 |
81 | templ IconPuzzle() {
82 |
88 |
94 | }
95 |
96 | templ IconContact() {
97 |
103 |
108 | }
109 |
110 | templ IconGithub() {
111 |
116 | }
117 |
118 | templ Footer() {
119 |
135 | }
136 |
--------------------------------------------------------------------------------
/components/App_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.2.680
4 | package components
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import "context"
10 | import "io"
11 | import "bytes"
12 |
13 | func App(route string) templ.Component {
14 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
15 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
16 | if !templ_7745c5c3_IsBuffer {
17 | templ_7745c5c3_Buffer = templ.GetBuffer()
18 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
19 | }
20 | ctx = templ.InitializeContext(ctx)
21 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
22 | if templ_7745c5c3_Var1 == nil {
23 | templ_7745c5c3_Var1 = templ.NopComponent
24 | }
25 | ctx = templ.ClearChildren(ctx)
26 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("GOAT Stack")
27 | if templ_7745c5c3_Err != nil {
28 | return templ_7745c5c3_Err
29 | }
30 | templ_7745c5c3_Err = Header().Render(ctx, templ_7745c5c3_Buffer)
31 | if templ_7745c5c3_Err != nil {
32 | return templ_7745c5c3_Err
33 | }
34 | switch route {
35 | case "/":
36 | templ_7745c5c3_Err = Home().Render(ctx, templ_7745c5c3_Buffer)
37 | if templ_7745c5c3_Err != nil {
38 | return templ_7745c5c3_Err
39 | }
40 | case "/examples":
41 | templ_7745c5c3_Err = Examples().Render(ctx, templ_7745c5c3_Buffer)
42 | if templ_7745c5c3_Err != nil {
43 | return templ_7745c5c3_Err
44 | }
45 | case "/contact":
46 | templ_7745c5c3_Err = Contact().Render(ctx, templ_7745c5c3_Buffer)
47 | if templ_7745c5c3_Err != nil {
48 | return templ_7745c5c3_Err
49 | }
50 | default:
51 | templ_7745c5c3_Err = Home().Render(ctx, templ_7745c5c3_Buffer)
52 | if templ_7745c5c3_Err != nil {
53 | return templ_7745c5c3_Err
54 | }
55 | }
56 | templ_7745c5c3_Err = Footer().Render(ctx, templ_7745c5c3_Buffer)
57 | if templ_7745c5c3_Err != nil {
58 | return templ_7745c5c3_Err
59 | }
60 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
61 | if templ_7745c5c3_Err != nil {
62 | return templ_7745c5c3_Err
63 | }
64 | if !templ_7745c5c3_IsBuffer {
65 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
66 | }
67 | return templ_7745c5c3_Err
68 | })
69 | }
70 |
71 | func Header() templ.Component {
72 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
73 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
74 | if !templ_7745c5c3_IsBuffer {
75 | templ_7745c5c3_Buffer = templ.GetBuffer()
76 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
77 | }
78 | ctx = templ.InitializeContext(ctx)
79 | templ_7745c5c3_Var2 := templ.GetChildren(ctx)
80 | if templ_7745c5c3_Var2 == nil {
81 | templ_7745c5c3_Var2 = templ.NopComponent
82 | }
83 | ctx = templ.ClearChildren(ctx)
84 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
109 | if templ_7745c5c3_Err != nil {
110 | return templ_7745c5c3_Err
111 | }
112 | if !templ_7745c5c3_IsBuffer {
113 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
114 | }
115 | return templ_7745c5c3_Err
116 | })
117 | }
118 |
119 | func IconPuzzle() templ.Component {
120 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
121 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
122 | if !templ_7745c5c3_IsBuffer {
123 | templ_7745c5c3_Buffer = templ.GetBuffer()
124 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
125 | }
126 | ctx = templ.InitializeContext(ctx)
127 | templ_7745c5c3_Var3 := templ.GetChildren(ctx)
128 | if templ_7745c5c3_Var3 == nil {
129 | templ_7745c5c3_Var3 = templ.NopComponent
130 | }
131 | ctx = templ.ClearChildren(ctx)
132 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
133 | if templ_7745c5c3_Err != nil {
134 | return templ_7745c5c3_Err
135 | }
136 | if !templ_7745c5c3_IsBuffer {
137 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
138 | }
139 | return templ_7745c5c3_Err
140 | })
141 | }
142 |
143 | func IconContact() templ.Component {
144 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
145 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
146 | if !templ_7745c5c3_IsBuffer {
147 | templ_7745c5c3_Buffer = templ.GetBuffer()
148 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
149 | }
150 | ctx = templ.InitializeContext(ctx)
151 | templ_7745c5c3_Var4 := templ.GetChildren(ctx)
152 | if templ_7745c5c3_Var4 == nil {
153 | templ_7745c5c3_Var4 = templ.NopComponent
154 | }
155 | ctx = templ.ClearChildren(ctx)
156 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
157 | if templ_7745c5c3_Err != nil {
158 | return templ_7745c5c3_Err
159 | }
160 | if !templ_7745c5c3_IsBuffer {
161 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
162 | }
163 | return templ_7745c5c3_Err
164 | })
165 | }
166 |
167 | func IconGithub() templ.Component {
168 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
169 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
170 | if !templ_7745c5c3_IsBuffer {
171 | templ_7745c5c3_Buffer = templ.GetBuffer()
172 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
173 | }
174 | ctx = templ.InitializeContext(ctx)
175 | templ_7745c5c3_Var5 := templ.GetChildren(ctx)
176 | if templ_7745c5c3_Var5 == nil {
177 | templ_7745c5c3_Var5 = templ.NopComponent
178 | }
179 | ctx = templ.ClearChildren(ctx)
180 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
181 | if templ_7745c5c3_Err != nil {
182 | return templ_7745c5c3_Err
183 | }
184 | if !templ_7745c5c3_IsBuffer {
185 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
186 | }
187 | return templ_7745c5c3_Err
188 | })
189 | }
190 |
191 | func Footer() templ.Component {
192 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
193 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
194 | if !templ_7745c5c3_IsBuffer {
195 | templ_7745c5c3_Buffer = templ.GetBuffer()
196 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
197 | }
198 | ctx = templ.InitializeContext(ctx)
199 | templ_7745c5c3_Var6 := templ.GetChildren(ctx)
200 | if templ_7745c5c3_Var6 == nil {
201 | templ_7745c5c3_Var6 = templ.NopComponent
202 | }
203 | ctx = templ.ClearChildren(ctx)
204 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
205 | if templ_7745c5c3_Err != nil {
206 | return templ_7745c5c3_Err
207 | }
208 | if !templ_7745c5c3_IsBuffer {
209 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
210 | }
211 | return templ_7745c5c3_Err
212 | })
213 | }
214 |
--------------------------------------------------------------------------------
/components/Contact.templ:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | templ Contact() {
4 |
5 |
6 |
Contact Us
7 |
Have questions or need support?
8 |
10 | Send an Email
11 |
12 |
Or email us directly at:
13 |
support@morethancoder.com
14 |
15 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/components/Contact_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.2.680
4 | package components
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import "context"
10 | import "io"
11 | import "bytes"
12 |
13 | func Contact() templ.Component {
14 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
15 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
16 | if !templ_7745c5c3_IsBuffer {
17 | templ_7745c5c3_Buffer = templ.GetBuffer()
18 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
19 | }
20 | ctx = templ.InitializeContext(ctx)
21 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
22 | if templ_7745c5c3_Var1 == nil {
23 | templ_7745c5c3_Var1 = templ.NopComponent
24 | }
25 | ctx = templ.ClearChildren(ctx)
26 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Contact Us
Have questions or need support?
Send an EmailOr email us directly at:
support@morethancoder.com
")
27 | if templ_7745c5c3_Err != nil {
28 | return templ_7745c5c3_Err
29 | }
30 | if !templ_7745c5c3_IsBuffer {
31 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
32 | }
33 | return templ_7745c5c3_Err
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/components/Examples.templ:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | templ Examples() {
4 |
5 |
6 | GOAT Stack Examples
7 | Explore the power of Go templates with Alpine.js and Tailwind CSS
8 |
9 |
10 |
11 | Button Component
12 |
13 | @Button("Primary", "bg-yellow-500 text-white", templ.Attributes{ "type": "submit" })
14 | @Button("Secondary", "bg-stone-200 text-stone-800 dark:bg-stone-700 dark:text-white", templ.Attributes{ "type": "button" })
15 | @Button("Outline", "border border-stone-800 text-stone-800 dark:border-stone-200 dark:text-stone-200", templ.Attributes{ "type": "button" })
16 |
17 | @CodeBlock(buttonCode)
18 |
19 |
20 |
21 | Toggle Switch
22 |
23 | @ToggleSwitch("Toggle Me")
24 |
25 | @CodeBlock(toggleSwitchCode)
26 |
27 |
28 |
29 | Counter
30 |
31 | @Counter()
32 |
33 | @CodeBlock(counterCode)
34 |
35 |
36 |
37 | Modal Dialog
38 |
39 | @Modal("Open Modal", "Modal Title", "This is the modal content.")
40 |
41 | @CodeBlock(modalCode)
42 |
43 |
44 | }
45 |
46 | templ Button(text string, classes string, attrs templ.Attributes) {
47 |
48 | }
49 |
50 | templ ToggleSwitch(label string) {
51 |
52 | { label }
53 |
66 |
67 |
68 | }
69 |
70 | templ Counter() {
71 |
72 |
Count:
73 |
74 | @Button("Increment", "bg-yellow-500 text-white", templ.Attributes{ "@click": "count++", "type": "submit" })
75 | @Button("Decrement", "bg-stone-200 text-stone-800 dark:bg-stone-700 dark:text-white", templ.Attributes{ "@click": "count--", "type": "button" })
76 |
77 |
78 |
79 | }
80 |
81 | templ Modal(buttonText, title, content string) {
82 |
83 |
85 |
86 |
87 |
88 |
{ title }
89 |
{ content }
90 |
92 |
93 |
94 |
95 | }
96 |
97 | templ CodeBlock(code string) {
98 |
99 |
100 | { code }
101 |
102 | @CopyButton(code)
103 |
104 | }
105 |
106 |
107 | templ CopyButton(code string) {
108 |
120 | }
121 |
122 |
123 | const (
124 | buttonCode = `templ Button(text string, classes string) {
125 |
126 | }
127 |
128 | // Usage in another template:
129 | @Button("Primary", "bg-blue-500 text-white")
130 | @Button("Secondary", "bg-gray-200 text-gray-800")
131 | @Button("Outline", "border border-gray-300 text-gray-700")`
132 |
133 | toggleSwitchCode = `templ ToggleSwitch(label string) {
134 |
135 | { label }
136 |
146 |
147 |
148 | }
149 |
150 | // Usage in another template:
151 | @ToggleSwitch("Toggle Me")`
152 |
153 | counterCode = `templ Counter() {
154 |
155 |
Count:
156 |
157 |
158 |
159 |
160 |
161 | }
162 |
163 | // Usage in another template:
164 | @Counter()`
165 |
166 | modalCode = `templ Modal(buttonText, title, content string) {
167 |
168 |
169 |
170 |
171 |
172 |
{ title }
173 |
{ content }
174 |
175 |
176 |
177 |
178 | }
179 |
180 | // Usage in another template:
181 | @Modal("Open Modal", "Modal Title", "This is the modal content.")`
182 | )
183 |
--------------------------------------------------------------------------------
/components/Examples_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.2.680
4 | package components
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import "context"
10 | import "io"
11 | import "bytes"
12 |
13 | func Examples() templ.Component {
14 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
15 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
16 | if !templ_7745c5c3_IsBuffer {
17 | templ_7745c5c3_Buffer = templ.GetBuffer()
18 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
19 | }
20 | ctx = templ.InitializeContext(ctx)
21 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
22 | if templ_7745c5c3_Var1 == nil {
23 | templ_7745c5c3_Var1 = templ.NopComponent
24 | }
25 | ctx = templ.ClearChildren(ctx)
26 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("GOAT Stack Examples
Explore the power of Go templates with Alpine.js and Tailwind CSS
Button Component
")
27 | if templ_7745c5c3_Err != nil {
28 | return templ_7745c5c3_Err
29 | }
30 | templ_7745c5c3_Err = Button("Primary", "bg-yellow-500 text-white", templ.Attributes{"type": "submit"}).Render(ctx, templ_7745c5c3_Buffer)
31 | if templ_7745c5c3_Err != nil {
32 | return templ_7745c5c3_Err
33 | }
34 | templ_7745c5c3_Err = Button("Secondary", "bg-stone-200 text-stone-800 dark:bg-stone-700 dark:text-white", templ.Attributes{"type": "button"}).Render(ctx, templ_7745c5c3_Buffer)
35 | if templ_7745c5c3_Err != nil {
36 | return templ_7745c5c3_Err
37 | }
38 | templ_7745c5c3_Err = Button("Outline", "border border-stone-800 text-stone-800 dark:border-stone-200 dark:text-stone-200", templ.Attributes{"type": "button"}).Render(ctx, templ_7745c5c3_Buffer)
39 | if templ_7745c5c3_Err != nil {
40 | return templ_7745c5c3_Err
41 | }
42 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
43 | if templ_7745c5c3_Err != nil {
44 | return templ_7745c5c3_Err
45 | }
46 | templ_7745c5c3_Err = CodeBlock(buttonCode).Render(ctx, templ_7745c5c3_Buffer)
47 | if templ_7745c5c3_Err != nil {
48 | return templ_7745c5c3_Err
49 | }
50 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Toggle Switch
")
51 | if templ_7745c5c3_Err != nil {
52 | return templ_7745c5c3_Err
53 | }
54 | templ_7745c5c3_Err = ToggleSwitch("Toggle Me").Render(ctx, templ_7745c5c3_Buffer)
55 | if templ_7745c5c3_Err != nil {
56 | return templ_7745c5c3_Err
57 | }
58 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
59 | if templ_7745c5c3_Err != nil {
60 | return templ_7745c5c3_Err
61 | }
62 | templ_7745c5c3_Err = CodeBlock(toggleSwitchCode).Render(ctx, templ_7745c5c3_Buffer)
63 | if templ_7745c5c3_Err != nil {
64 | return templ_7745c5c3_Err
65 | }
66 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Counter
")
67 | if templ_7745c5c3_Err != nil {
68 | return templ_7745c5c3_Err
69 | }
70 | templ_7745c5c3_Err = Counter().Render(ctx, templ_7745c5c3_Buffer)
71 | if templ_7745c5c3_Err != nil {
72 | return templ_7745c5c3_Err
73 | }
74 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
75 | if templ_7745c5c3_Err != nil {
76 | return templ_7745c5c3_Err
77 | }
78 | templ_7745c5c3_Err = CodeBlock(counterCode).Render(ctx, templ_7745c5c3_Buffer)
79 | if templ_7745c5c3_Err != nil {
80 | return templ_7745c5c3_Err
81 | }
82 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Modal Dialog
")
83 | if templ_7745c5c3_Err != nil {
84 | return templ_7745c5c3_Err
85 | }
86 | templ_7745c5c3_Err = Modal("Open Modal", "Modal Title", "This is the modal content.").Render(ctx, templ_7745c5c3_Buffer)
87 | if templ_7745c5c3_Err != nil {
88 | return templ_7745c5c3_Err
89 | }
90 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
91 | if templ_7745c5c3_Err != nil {
92 | return templ_7745c5c3_Err
93 | }
94 | templ_7745c5c3_Err = CodeBlock(modalCode).Render(ctx, templ_7745c5c3_Buffer)
95 | if templ_7745c5c3_Err != nil {
96 | return templ_7745c5c3_Err
97 | }
98 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
99 | if templ_7745c5c3_Err != nil {
100 | return templ_7745c5c3_Err
101 | }
102 | if !templ_7745c5c3_IsBuffer {
103 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
104 | }
105 | return templ_7745c5c3_Err
106 | })
107 | }
108 |
109 | func Button(text string, classes string, attrs templ.Attributes) templ.Component {
110 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
111 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
112 | if !templ_7745c5c3_IsBuffer {
113 | templ_7745c5c3_Buffer = templ.GetBuffer()
114 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
115 | }
116 | ctx = templ.InitializeContext(ctx)
117 | templ_7745c5c3_Var2 := templ.GetChildren(ctx)
118 | if templ_7745c5c3_Var2 == nil {
119 | templ_7745c5c3_Var2 = templ.NopComponent
120 | }
121 | ctx = templ.ClearChildren(ctx)
122 | var templ_7745c5c3_Var3 = []any{"px-4 py-2 rounded hover:opacity-80 transition duration-300 " + classes}
123 | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
124 | if templ_7745c5c3_Err != nil {
125 | return templ_7745c5c3_Err
126 | }
127 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
162 | if templ_7745c5c3_Err != nil {
163 | return templ_7745c5c3_Err
164 | }
165 | if !templ_7745c5c3_IsBuffer {
166 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
167 | }
168 | return templ_7745c5c3_Err
169 | })
170 | }
171 |
172 | func ToggleSwitch(label string) templ.Component {
173 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
174 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
175 | if !templ_7745c5c3_IsBuffer {
176 | templ_7745c5c3_Buffer = templ.GetBuffer()
177 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
178 | }
179 | ctx = templ.InitializeContext(ctx)
180 | templ_7745c5c3_Var6 := templ.GetChildren(ctx)
181 | if templ_7745c5c3_Var6 == nil {
182 | templ_7745c5c3_Var6 = templ.NopComponent
183 | }
184 | ctx = templ.ClearChildren(ctx)
185 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
186 | if templ_7745c5c3_Err != nil {
187 | return templ_7745c5c3_Err
188 | }
189 | var templ_7745c5c3_Var7 string
190 | templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(label)
191 | if templ_7745c5c3_Err != nil {
192 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/Examples.templ`, Line: 52, Col: 84}
193 | }
194 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
195 | if templ_7745c5c3_Err != nil {
196 | return templ_7745c5c3_Err
197 | }
198 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
199 | if templ_7745c5c3_Err != nil {
200 | return templ_7745c5c3_Err
201 | }
202 | if !templ_7745c5c3_IsBuffer {
203 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
204 | }
205 | return templ_7745c5c3_Err
206 | })
207 | }
208 |
209 | func Counter() templ.Component {
210 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
211 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
212 | if !templ_7745c5c3_IsBuffer {
213 | templ_7745c5c3_Buffer = templ.GetBuffer()
214 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
215 | }
216 | ctx = templ.InitializeContext(ctx)
217 | templ_7745c5c3_Var8 := templ.GetChildren(ctx)
218 | if templ_7745c5c3_Var8 == nil {
219 | templ_7745c5c3_Var8 = templ.NopComponent
220 | }
221 | ctx = templ.ClearChildren(ctx)
222 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Count:
")
223 | if templ_7745c5c3_Err != nil {
224 | return templ_7745c5c3_Err
225 | }
226 | templ_7745c5c3_Err = Button("Increment", "bg-yellow-500 text-white", templ.Attributes{"@click": "count++", "type": "submit"}).Render(ctx, templ_7745c5c3_Buffer)
227 | if templ_7745c5c3_Err != nil {
228 | return templ_7745c5c3_Err
229 | }
230 | templ_7745c5c3_Err = Button("Decrement", "bg-stone-200 text-stone-800 dark:bg-stone-700 dark:text-white", templ.Attributes{"@click": "count--", "type": "button"}).Render(ctx, templ_7745c5c3_Buffer)
231 | if templ_7745c5c3_Err != nil {
232 | return templ_7745c5c3_Err
233 | }
234 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
235 | if templ_7745c5c3_Err != nil {
236 | return templ_7745c5c3_Err
237 | }
238 | if !templ_7745c5c3_IsBuffer {
239 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
240 | }
241 | return templ_7745c5c3_Err
242 | })
243 | }
244 |
245 | func Modal(buttonText, title, content string) templ.Component {
246 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
247 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
248 | if !templ_7745c5c3_IsBuffer {
249 | templ_7745c5c3_Buffer = templ.GetBuffer()
250 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
251 | }
252 | ctx = templ.InitializeContext(ctx)
253 | templ_7745c5c3_Var9 := templ.GetChildren(ctx)
254 | if templ_7745c5c3_Var9 == nil {
255 | templ_7745c5c3_Var9 = templ.NopComponent
256 | }
257 | ctx = templ.ClearChildren(ctx)
258 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
272 | if templ_7745c5c3_Err != nil {
273 | return templ_7745c5c3_Err
274 | }
275 | var templ_7745c5c3_Var11 string
276 | templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(title)
277 | if templ_7745c5c3_Err != nil {
278 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/Examples.templ`, Line: 88, Col: 62}
279 | }
280 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
281 | if templ_7745c5c3_Err != nil {
282 | return templ_7745c5c3_Err
283 | }
284 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
285 | if templ_7745c5c3_Err != nil {
286 | return templ_7745c5c3_Err
287 | }
288 | var templ_7745c5c3_Var12 string
289 | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(content)
290 | if templ_7745c5c3_Err != nil {
291 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/Examples.templ`, Line: 89, Col: 41}
292 | }
293 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
294 | if templ_7745c5c3_Err != nil {
295 | return templ_7745c5c3_Err
296 | }
297 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
298 | if templ_7745c5c3_Err != nil {
299 | return templ_7745c5c3_Err
300 | }
301 | if !templ_7745c5c3_IsBuffer {
302 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
303 | }
304 | return templ_7745c5c3_Err
305 | })
306 | }
307 |
308 | func CodeBlock(code string) templ.Component {
309 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
310 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
311 | if !templ_7745c5c3_IsBuffer {
312 | templ_7745c5c3_Buffer = templ.GetBuffer()
313 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
314 | }
315 | ctx = templ.InitializeContext(ctx)
316 | templ_7745c5c3_Var13 := templ.GetChildren(ctx)
317 | if templ_7745c5c3_Var13 == nil {
318 | templ_7745c5c3_Var13 = templ.NopComponent
319 | }
320 | ctx = templ.ClearChildren(ctx)
321 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
322 | if templ_7745c5c3_Err != nil {
323 | return templ_7745c5c3_Err
324 | }
325 | var templ_7745c5c3_Var14 string
326 | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(code)
327 | if templ_7745c5c3_Err != nil {
328 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/Examples.templ`, Line: 100, Col: 60}
329 | }
330 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
331 | if templ_7745c5c3_Err != nil {
332 | return templ_7745c5c3_Err
333 | }
334 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
335 | if templ_7745c5c3_Err != nil {
336 | return templ_7745c5c3_Err
337 | }
338 | templ_7745c5c3_Err = CopyButton(code).Render(ctx, templ_7745c5c3_Buffer)
339 | if templ_7745c5c3_Err != nil {
340 | return templ_7745c5c3_Err
341 | }
342 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
343 | if templ_7745c5c3_Err != nil {
344 | return templ_7745c5c3_Err
345 | }
346 | if !templ_7745c5c3_IsBuffer {
347 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
348 | }
349 | return templ_7745c5c3_Err
350 | })
351 | }
352 |
353 | func CopyButton(code string) templ.Component {
354 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
355 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
356 | if !templ_7745c5c3_IsBuffer {
357 | templ_7745c5c3_Buffer = templ.GetBuffer()
358 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
359 | }
360 | ctx = templ.InitializeContext(ctx)
361 | templ_7745c5c3_Var15 := templ.GetChildren(ctx)
362 | if templ_7745c5c3_Var15 == nil {
363 | templ_7745c5c3_Var15 = templ.NopComponent
364 | }
365 | ctx = templ.ClearChildren(ctx)
366 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
388 | if templ_7745c5c3_Err != nil {
389 | return templ_7745c5c3_Err
390 | }
391 | if !templ_7745c5c3_IsBuffer {
392 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
393 | }
394 | return templ_7745c5c3_Err
395 | })
396 | }
397 |
398 | const (
399 | buttonCode = `templ Button(text string, classes string) {
400 |
401 | }
402 |
403 | // Usage in another template:
404 | @Button("Primary", "bg-blue-500 text-white")
405 | @Button("Secondary", "bg-gray-200 text-gray-800")
406 | @Button("Outline", "border border-gray-300 text-gray-700")`
407 |
408 | toggleSwitchCode = `templ ToggleSwitch(label string) {
409 |
410 | { label }
411 |
421 |
422 |
423 | }
424 |
425 | // Usage in another template:
426 | @ToggleSwitch("Toggle Me")`
427 |
428 | counterCode = `templ Counter() {
429 |
430 |
Count:
431 |
432 |
433 |
434 |
435 |
436 | }
437 |
438 | // Usage in another template:
439 | @Counter()`
440 |
441 | modalCode = `templ Modal(buttonText, title, content string) {
442 |
443 |
444 |
445 |
446 |
447 |
{ title }
448 |
{ content }
449 |
450 |
451 |
452 |
453 | }
454 |
455 | // Usage in another template:
456 | @Modal("Open Modal", "Modal Title", "This is the modal content.")`
457 | )
458 |
--------------------------------------------------------------------------------
/components/Home.templ:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | templ Home() {
4 |
5 |
6 | GOAT Stack
7 | Blazing fast web development with Go, Alpine.js, and Tailwind CSS
8 |
12 |
28 |
36 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
61 |
62 |
63 | Tech Stack
64 |
65 |
68 |
69 | @IconLayers()
70 |
Go with Templ
71 |
72 | Powerful backend for server-side rendering and rapid static page generation.
73 |
74 |
77 |
78 | @IconLightning()
79 |
Alpine.js
80 |
81 | Lightweight JavaScript framework for smooth interactivity.
82 |
83 |
86 |
87 | @IconPaintbrush()
88 |
Tailwind CSS
89 |
90 | Utility-first CSS framework for rapid UI development.
91 |
92 |
95 |
96 | @IconRocket()
97 |
Vite (optional)
98 |
99 | Next-generation frontend tooling for fast hot reloading.
100 |
101 |
102 |
103 |
104 | Get Started
105 |
106 | -
107 |
108 | @IconGitBranch()
109 |
110 |
111 |
Clone the repo:
112 |
113 |
114 |
115 | git clone https://github.com/morethancoder/goat.git yourprojectname
116 |
117 |
118 | @CopyButton("git clone https://github.com/morethancoder/goat.git yourprojectname")
119 |
120 |
121 |
122 | -
123 |
124 | @IconCode()
125 |
126 |
127 |
Change into the project directory:
128 |
129 |
130 |
131 | cd yourprojectname
132 |
133 |
134 | @CopyButton(`cd yourprojectname`)
135 |
136 |
137 |
138 | -
139 |
140 | @IconPackage()
141 |
142 |
143 |
Initialize the project:
144 |
145 |
146 |
147 | go mod init yourprojectname && go mod tidy
148 |
149 |
150 | @CopyButton("go mod init yourprojectname && go mod tidy")
151 |
152 |
153 |
154 | -
155 |
156 | @IconCode()
157 |
158 |
159 |
Change the import in main.go:
160 |
161 |
162 |
163 | { `// Change this:
164 | "morethancoder/goat/components"
165 | // To:
166 | "yourprojectname/components"` }
167 |
168 |
169 | @CopyButton(`// Change this:
170 | "morethancoder/goat/components"
171 | // To:
172 | "yourprojectname/components"`)
173 |
174 |
175 |
176 | -
177 |
178 | @IconDownload()
179 |
180 |
181 |
Install required Node modules:
182 |
183 |
184 |
185 | bun install
186 |
187 |
188 | @CopyButton("bun install")
189 |
190 |
191 |
192 | -
193 |
194 | @IconPlay()
195 |
196 |
197 |
Start the Vite dev server:
198 |
199 |
200 |
201 | make vite
202 |
203 |
204 | @CopyButton("make vite")
205 |
206 |
207 |
208 | -
209 |
210 | @IconEye()
211 |
212 |
213 |
Start file watching & compiling:
214 |
215 |
216 |
217 | make watch
218 |
219 |
220 | @CopyButton("make watch")
221 |
222 |
223 |
224 | -
225 |
226 | @IconStar()
227 |
228 |
229 |
Star the repo!
230 |
231 | If you find this project helpful, please consider giving it a star on
232 |
234 | GitHub.
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 | }
243 |
244 | templ IconPlay() {
245 |
251 | }
252 |
253 | templ IconPaintbrush() {
254 |
260 | }
261 |
262 | templ IconRocket() {
263 |
269 | }
270 |
271 | templ IconCode() {
272 |
277 | }
278 |
279 | templ IconStar() {
280 |
286 | }
287 |
288 | templ IconBook() {
289 |
295 | }
296 |
297 | templ IconLightning() {
298 |
303 | }
304 |
305 | templ IconCloud() {
306 |
312 | }
313 |
314 | templ IconLayers() {
315 |
321 | }
322 |
323 | templ IconGitBranch() {
324 |
329 | }
330 |
331 | templ IconPackage() {
332 |
338 | }
339 |
340 | templ IconDownload() {
341 |
347 | }
348 |
349 | templ IconEye() {
350 |
357 | }
358 |
359 | templ IconCopy() {
360 |
366 | }
367 |
--------------------------------------------------------------------------------
/components/Home_templ.go:
--------------------------------------------------------------------------------
1 | // Code generated by templ - DO NOT EDIT.
2 |
3 | // templ: version: v0.2.680
4 | package components
5 |
6 | //lint:file-ignore SA4006 This context is only used if a nested component is present.
7 |
8 | import "github.com/a-h/templ"
9 | import "context"
10 | import "io"
11 | import "bytes"
12 |
13 | func Home() templ.Component {
14 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
15 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
16 | if !templ_7745c5c3_IsBuffer {
17 | templ_7745c5c3_Buffer = templ.GetBuffer()
18 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
19 | }
20 | ctx = templ.InitializeContext(ctx)
21 | templ_7745c5c3_Var1 := templ.GetChildren(ctx)
22 | if templ_7745c5c3_Var1 == nil {
23 | templ_7745c5c3_Var1 = templ.NopComponent
24 | }
25 | ctx = templ.ClearChildren(ctx)
26 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Tech Stack
")
43 | if templ_7745c5c3_Err != nil {
44 | return templ_7745c5c3_Err
45 | }
46 | templ_7745c5c3_Err = IconLayers().Render(ctx, templ_7745c5c3_Buffer)
47 | if templ_7745c5c3_Err != nil {
48 | return templ_7745c5c3_Err
49 | }
50 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Go with Templ
Powerful backend for server-side rendering and rapid static page generation.
")
51 | if templ_7745c5c3_Err != nil {
52 | return templ_7745c5c3_Err
53 | }
54 | templ_7745c5c3_Err = IconLightning().Render(ctx, templ_7745c5c3_Buffer)
55 | if templ_7745c5c3_Err != nil {
56 | return templ_7745c5c3_Err
57 | }
58 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Alpine.js
Lightweight JavaScript framework for smooth interactivity.
")
59 | if templ_7745c5c3_Err != nil {
60 | return templ_7745c5c3_Err
61 | }
62 | templ_7745c5c3_Err = IconPaintbrush().Render(ctx, templ_7745c5c3_Buffer)
63 | if templ_7745c5c3_Err != nil {
64 | return templ_7745c5c3_Err
65 | }
66 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Tailwind CSS
Utility-first CSS framework for rapid UI development.
")
67 | if templ_7745c5c3_Err != nil {
68 | return templ_7745c5c3_Err
69 | }
70 | templ_7745c5c3_Err = IconRocket().Render(ctx, templ_7745c5c3_Buffer)
71 | if templ_7745c5c3_Err != nil {
72 | return templ_7745c5c3_Err
73 | }
74 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Vite (optional)
Next-generation frontend tooling for fast hot reloading.
Get Started
")
75 | if templ_7745c5c3_Err != nil {
76 | return templ_7745c5c3_Err
77 | }
78 | templ_7745c5c3_Err = IconGitBranch().Render(ctx, templ_7745c5c3_Buffer)
79 | if templ_7745c5c3_Err != nil {
80 | return templ_7745c5c3_Err
81 | }
82 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Clone the repo:
git clone https://github.com/morethancoder/goat.git yourprojectname
")
83 | if templ_7745c5c3_Err != nil {
84 | return templ_7745c5c3_Err
85 | }
86 | templ_7745c5c3_Err = CopyButton("git clone https://github.com/morethancoder/goat.git yourprojectname").Render(ctx, templ_7745c5c3_Buffer)
87 | if templ_7745c5c3_Err != nil {
88 | return templ_7745c5c3_Err
89 | }
90 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
91 | if templ_7745c5c3_Err != nil {
92 | return templ_7745c5c3_Err
93 | }
94 | templ_7745c5c3_Err = IconCode().Render(ctx, templ_7745c5c3_Buffer)
95 | if templ_7745c5c3_Err != nil {
96 | return templ_7745c5c3_Err
97 | }
98 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Change into the project directory:
cd yourprojectname
")
99 | if templ_7745c5c3_Err != nil {
100 | return templ_7745c5c3_Err
101 | }
102 | templ_7745c5c3_Err = CopyButton(`cd yourprojectname`).Render(ctx, templ_7745c5c3_Buffer)
103 | if templ_7745c5c3_Err != nil {
104 | return templ_7745c5c3_Err
105 | }
106 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
107 | if templ_7745c5c3_Err != nil {
108 | return templ_7745c5c3_Err
109 | }
110 | templ_7745c5c3_Err = IconPackage().Render(ctx, templ_7745c5c3_Buffer)
111 | if templ_7745c5c3_Err != nil {
112 | return templ_7745c5c3_Err
113 | }
114 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Initialize the project:
go mod init yourprojectname && go mod tidy
")
115 | if templ_7745c5c3_Err != nil {
116 | return templ_7745c5c3_Err
117 | }
118 | templ_7745c5c3_Err = CopyButton("go mod init yourprojectname && go mod tidy").Render(ctx, templ_7745c5c3_Buffer)
119 | if templ_7745c5c3_Err != nil {
120 | return templ_7745c5c3_Err
121 | }
122 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
123 | if templ_7745c5c3_Err != nil {
124 | return templ_7745c5c3_Err
125 | }
126 | templ_7745c5c3_Err = IconCode().Render(ctx, templ_7745c5c3_Buffer)
127 | if templ_7745c5c3_Err != nil {
128 | return templ_7745c5c3_Err
129 | }
130 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Change the import in main.go:
")
131 | if templ_7745c5c3_Err != nil {
132 | return templ_7745c5c3_Err
133 | }
134 | var templ_7745c5c3_Var2 string
135 | templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(`// Change this:
136 | "morethancoder/goat/components"
137 | // To:
138 | "yourprojectname/components"`)
139 | if templ_7745c5c3_Err != nil {
140 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/Home.templ`, Line: 166, Col: 29}
141 | }
142 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
143 | if templ_7745c5c3_Err != nil {
144 | return templ_7745c5c3_Err
145 | }
146 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
147 | if templ_7745c5c3_Err != nil {
148 | return templ_7745c5c3_Err
149 | }
150 | templ_7745c5c3_Err = CopyButton(`// Change this:
151 | "morethancoder/goat/components"
152 | // To:
153 | "yourprojectname/components"`).Render(ctx, templ_7745c5c3_Buffer)
154 | if templ_7745c5c3_Err != nil {
155 | return templ_7745c5c3_Err
156 | }
157 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
158 | if templ_7745c5c3_Err != nil {
159 | return templ_7745c5c3_Err
160 | }
161 | templ_7745c5c3_Err = IconDownload().Render(ctx, templ_7745c5c3_Buffer)
162 | if templ_7745c5c3_Err != nil {
163 | return templ_7745c5c3_Err
164 | }
165 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Install required Node modules:
bun install
")
166 | if templ_7745c5c3_Err != nil {
167 | return templ_7745c5c3_Err
168 | }
169 | templ_7745c5c3_Err = CopyButton("bun install").Render(ctx, templ_7745c5c3_Buffer)
170 | if templ_7745c5c3_Err != nil {
171 | return templ_7745c5c3_Err
172 | }
173 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
174 | if templ_7745c5c3_Err != nil {
175 | return templ_7745c5c3_Err
176 | }
177 | templ_7745c5c3_Err = IconPlay().Render(ctx, templ_7745c5c3_Buffer)
178 | if templ_7745c5c3_Err != nil {
179 | return templ_7745c5c3_Err
180 | }
181 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Start the Vite dev server:
make vite
")
182 | if templ_7745c5c3_Err != nil {
183 | return templ_7745c5c3_Err
184 | }
185 | templ_7745c5c3_Err = CopyButton("make vite").Render(ctx, templ_7745c5c3_Buffer)
186 | if templ_7745c5c3_Err != nil {
187 | return templ_7745c5c3_Err
188 | }
189 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
190 | if templ_7745c5c3_Err != nil {
191 | return templ_7745c5c3_Err
192 | }
193 | templ_7745c5c3_Err = IconEye().Render(ctx, templ_7745c5c3_Buffer)
194 | if templ_7745c5c3_Err != nil {
195 | return templ_7745c5c3_Err
196 | }
197 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Start file watching & compiling:
make watch
")
198 | if templ_7745c5c3_Err != nil {
199 | return templ_7745c5c3_Err
200 | }
201 | templ_7745c5c3_Err = CopyButton("make watch").Render(ctx, templ_7745c5c3_Buffer)
202 | if templ_7745c5c3_Err != nil {
203 | return templ_7745c5c3_Err
204 | }
205 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
206 | if templ_7745c5c3_Err != nil {
207 | return templ_7745c5c3_Err
208 | }
209 | templ_7745c5c3_Err = IconStar().Render(ctx, templ_7745c5c3_Buffer)
210 | if templ_7745c5c3_Err != nil {
211 | return templ_7745c5c3_Err
212 | }
213 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Star the repo!
If you find this project helpful, please consider giving it a star on GitHub.
")
214 | if templ_7745c5c3_Err != nil {
215 | return templ_7745c5c3_Err
216 | }
217 | if !templ_7745c5c3_IsBuffer {
218 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
219 | }
220 | return templ_7745c5c3_Err
221 | })
222 | }
223 |
224 | func IconPlay() templ.Component {
225 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
226 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
227 | if !templ_7745c5c3_IsBuffer {
228 | templ_7745c5c3_Buffer = templ.GetBuffer()
229 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
230 | }
231 | ctx = templ.InitializeContext(ctx)
232 | templ_7745c5c3_Var3 := templ.GetChildren(ctx)
233 | if templ_7745c5c3_Var3 == nil {
234 | templ_7745c5c3_Var3 = templ.NopComponent
235 | }
236 | ctx = templ.ClearChildren(ctx)
237 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
238 | if templ_7745c5c3_Err != nil {
239 | return templ_7745c5c3_Err
240 | }
241 | if !templ_7745c5c3_IsBuffer {
242 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
243 | }
244 | return templ_7745c5c3_Err
245 | })
246 | }
247 |
248 | func IconPaintbrush() templ.Component {
249 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
250 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
251 | if !templ_7745c5c3_IsBuffer {
252 | templ_7745c5c3_Buffer = templ.GetBuffer()
253 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
254 | }
255 | ctx = templ.InitializeContext(ctx)
256 | templ_7745c5c3_Var4 := templ.GetChildren(ctx)
257 | if templ_7745c5c3_Var4 == nil {
258 | templ_7745c5c3_Var4 = templ.NopComponent
259 | }
260 | ctx = templ.ClearChildren(ctx)
261 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
262 | if templ_7745c5c3_Err != nil {
263 | return templ_7745c5c3_Err
264 | }
265 | if !templ_7745c5c3_IsBuffer {
266 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
267 | }
268 | return templ_7745c5c3_Err
269 | })
270 | }
271 |
272 | func IconRocket() templ.Component {
273 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
274 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
275 | if !templ_7745c5c3_IsBuffer {
276 | templ_7745c5c3_Buffer = templ.GetBuffer()
277 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
278 | }
279 | ctx = templ.InitializeContext(ctx)
280 | templ_7745c5c3_Var5 := templ.GetChildren(ctx)
281 | if templ_7745c5c3_Var5 == nil {
282 | templ_7745c5c3_Var5 = templ.NopComponent
283 | }
284 | ctx = templ.ClearChildren(ctx)
285 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
286 | if templ_7745c5c3_Err != nil {
287 | return templ_7745c5c3_Err
288 | }
289 | if !templ_7745c5c3_IsBuffer {
290 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
291 | }
292 | return templ_7745c5c3_Err
293 | })
294 | }
295 |
296 | func IconCode() templ.Component {
297 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
298 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
299 | if !templ_7745c5c3_IsBuffer {
300 | templ_7745c5c3_Buffer = templ.GetBuffer()
301 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
302 | }
303 | ctx = templ.InitializeContext(ctx)
304 | templ_7745c5c3_Var6 := templ.GetChildren(ctx)
305 | if templ_7745c5c3_Var6 == nil {
306 | templ_7745c5c3_Var6 = templ.NopComponent
307 | }
308 | ctx = templ.ClearChildren(ctx)
309 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
310 | if templ_7745c5c3_Err != nil {
311 | return templ_7745c5c3_Err
312 | }
313 | if !templ_7745c5c3_IsBuffer {
314 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
315 | }
316 | return templ_7745c5c3_Err
317 | })
318 | }
319 |
320 | func IconStar() templ.Component {
321 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
322 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
323 | if !templ_7745c5c3_IsBuffer {
324 | templ_7745c5c3_Buffer = templ.GetBuffer()
325 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
326 | }
327 | ctx = templ.InitializeContext(ctx)
328 | templ_7745c5c3_Var7 := templ.GetChildren(ctx)
329 | if templ_7745c5c3_Var7 == nil {
330 | templ_7745c5c3_Var7 = templ.NopComponent
331 | }
332 | ctx = templ.ClearChildren(ctx)
333 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
334 | if templ_7745c5c3_Err != nil {
335 | return templ_7745c5c3_Err
336 | }
337 | if !templ_7745c5c3_IsBuffer {
338 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
339 | }
340 | return templ_7745c5c3_Err
341 | })
342 | }
343 |
344 | func IconBook() templ.Component {
345 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
346 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
347 | if !templ_7745c5c3_IsBuffer {
348 | templ_7745c5c3_Buffer = templ.GetBuffer()
349 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
350 | }
351 | ctx = templ.InitializeContext(ctx)
352 | templ_7745c5c3_Var8 := templ.GetChildren(ctx)
353 | if templ_7745c5c3_Var8 == nil {
354 | templ_7745c5c3_Var8 = templ.NopComponent
355 | }
356 | ctx = templ.ClearChildren(ctx)
357 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
358 | if templ_7745c5c3_Err != nil {
359 | return templ_7745c5c3_Err
360 | }
361 | if !templ_7745c5c3_IsBuffer {
362 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
363 | }
364 | return templ_7745c5c3_Err
365 | })
366 | }
367 |
368 | func IconLightning() templ.Component {
369 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
370 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
371 | if !templ_7745c5c3_IsBuffer {
372 | templ_7745c5c3_Buffer = templ.GetBuffer()
373 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
374 | }
375 | ctx = templ.InitializeContext(ctx)
376 | templ_7745c5c3_Var9 := templ.GetChildren(ctx)
377 | if templ_7745c5c3_Var9 == nil {
378 | templ_7745c5c3_Var9 = templ.NopComponent
379 | }
380 | ctx = templ.ClearChildren(ctx)
381 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
382 | if templ_7745c5c3_Err != nil {
383 | return templ_7745c5c3_Err
384 | }
385 | if !templ_7745c5c3_IsBuffer {
386 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
387 | }
388 | return templ_7745c5c3_Err
389 | })
390 | }
391 |
392 | func IconCloud() templ.Component {
393 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
394 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
395 | if !templ_7745c5c3_IsBuffer {
396 | templ_7745c5c3_Buffer = templ.GetBuffer()
397 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
398 | }
399 | ctx = templ.InitializeContext(ctx)
400 | templ_7745c5c3_Var10 := templ.GetChildren(ctx)
401 | if templ_7745c5c3_Var10 == nil {
402 | templ_7745c5c3_Var10 = templ.NopComponent
403 | }
404 | ctx = templ.ClearChildren(ctx)
405 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
406 | if templ_7745c5c3_Err != nil {
407 | return templ_7745c5c3_Err
408 | }
409 | if !templ_7745c5c3_IsBuffer {
410 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
411 | }
412 | return templ_7745c5c3_Err
413 | })
414 | }
415 |
416 | func IconLayers() templ.Component {
417 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
418 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
419 | if !templ_7745c5c3_IsBuffer {
420 | templ_7745c5c3_Buffer = templ.GetBuffer()
421 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
422 | }
423 | ctx = templ.InitializeContext(ctx)
424 | templ_7745c5c3_Var11 := templ.GetChildren(ctx)
425 | if templ_7745c5c3_Var11 == nil {
426 | templ_7745c5c3_Var11 = templ.NopComponent
427 | }
428 | ctx = templ.ClearChildren(ctx)
429 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
430 | if templ_7745c5c3_Err != nil {
431 | return templ_7745c5c3_Err
432 | }
433 | if !templ_7745c5c3_IsBuffer {
434 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
435 | }
436 | return templ_7745c5c3_Err
437 | })
438 | }
439 |
440 | func IconGitBranch() templ.Component {
441 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
442 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
443 | if !templ_7745c5c3_IsBuffer {
444 | templ_7745c5c3_Buffer = templ.GetBuffer()
445 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
446 | }
447 | ctx = templ.InitializeContext(ctx)
448 | templ_7745c5c3_Var12 := templ.GetChildren(ctx)
449 | if templ_7745c5c3_Var12 == nil {
450 | templ_7745c5c3_Var12 = templ.NopComponent
451 | }
452 | ctx = templ.ClearChildren(ctx)
453 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
454 | if templ_7745c5c3_Err != nil {
455 | return templ_7745c5c3_Err
456 | }
457 | if !templ_7745c5c3_IsBuffer {
458 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
459 | }
460 | return templ_7745c5c3_Err
461 | })
462 | }
463 |
464 | func IconPackage() templ.Component {
465 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
466 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
467 | if !templ_7745c5c3_IsBuffer {
468 | templ_7745c5c3_Buffer = templ.GetBuffer()
469 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
470 | }
471 | ctx = templ.InitializeContext(ctx)
472 | templ_7745c5c3_Var13 := templ.GetChildren(ctx)
473 | if templ_7745c5c3_Var13 == nil {
474 | templ_7745c5c3_Var13 = templ.NopComponent
475 | }
476 | ctx = templ.ClearChildren(ctx)
477 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
478 | if templ_7745c5c3_Err != nil {
479 | return templ_7745c5c3_Err
480 | }
481 | if !templ_7745c5c3_IsBuffer {
482 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
483 | }
484 | return templ_7745c5c3_Err
485 | })
486 | }
487 |
488 | func IconDownload() templ.Component {
489 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
490 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
491 | if !templ_7745c5c3_IsBuffer {
492 | templ_7745c5c3_Buffer = templ.GetBuffer()
493 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
494 | }
495 | ctx = templ.InitializeContext(ctx)
496 | templ_7745c5c3_Var14 := templ.GetChildren(ctx)
497 | if templ_7745c5c3_Var14 == nil {
498 | templ_7745c5c3_Var14 = templ.NopComponent
499 | }
500 | ctx = templ.ClearChildren(ctx)
501 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
502 | if templ_7745c5c3_Err != nil {
503 | return templ_7745c5c3_Err
504 | }
505 | if !templ_7745c5c3_IsBuffer {
506 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
507 | }
508 | return templ_7745c5c3_Err
509 | })
510 | }
511 |
512 | func IconEye() templ.Component {
513 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
514 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
515 | if !templ_7745c5c3_IsBuffer {
516 | templ_7745c5c3_Buffer = templ.GetBuffer()
517 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
518 | }
519 | ctx = templ.InitializeContext(ctx)
520 | templ_7745c5c3_Var15 := templ.GetChildren(ctx)
521 | if templ_7745c5c3_Var15 == nil {
522 | templ_7745c5c3_Var15 = templ.NopComponent
523 | }
524 | ctx = templ.ClearChildren(ctx)
525 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
526 | if templ_7745c5c3_Err != nil {
527 | return templ_7745c5c3_Err
528 | }
529 | if !templ_7745c5c3_IsBuffer {
530 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
531 | }
532 | return templ_7745c5c3_Err
533 | })
534 | }
535 |
536 | func IconCopy() templ.Component {
537 | return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
538 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
539 | if !templ_7745c5c3_IsBuffer {
540 | templ_7745c5c3_Buffer = templ.GetBuffer()
541 | defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
542 | }
543 | ctx = templ.InitializeContext(ctx)
544 | templ_7745c5c3_Var16 := templ.GetChildren(ctx)
545 | if templ_7745c5c3_Var16 == nil {
546 | templ_7745c5c3_Var16 = templ.NopComponent
547 | }
548 | ctx = templ.ClearChildren(ctx)
549 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
550 | if templ_7745c5c3_Err != nil {
551 | return templ_7745c5c3_Err
552 | }
553 | if !templ_7745c5c3_IsBuffer {
554 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
555 | }
556 | return templ_7745c5c3_Err
557 | })
558 | }
559 |
--------------------------------------------------------------------------------
/input.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "morethancoder/goat/components"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 | "time"
11 |
12 | "github.com/charmbracelet/lipgloss"
13 | "github.com/charmbracelet/log"
14 | )
15 |
16 | var (
17 | // Define App Routes & Public Directory
18 | routes = []string{"/", "/examples", "/contact"}
19 | publicDir = "public"
20 |
21 | // Logger & Styles
22 | gopherStyle = lipgloss.NewStyle().
23 | Bold(true).
24 | Foreground(lipgloss.Color("#00FFFF"))
25 | goatStyle = lipgloss.NewStyle().
26 | Bold(true).
27 | Foreground(lipgloss.Color("#FFFF00"))
28 |
29 | logger = log.NewWithOptions(os.Stderr, log.Options{
30 | ReportCaller: true,
31 | ReportTimestamp: true,
32 | TimeFormat: time.Kitchen,
33 | Prefix: goatStyle.Render("Maa 🐐 "),
34 | })
35 | styles = log.DefaultStyles()
36 | )
37 |
38 | func main() {
39 | // Fancy logging
40 | styles.Levels[log.InfoLevel] = lipgloss.NewStyle().Bold(true).
41 | SetString("INFO").
42 | Padding(0, 1, 0, 1).
43 | Background(lipgloss.Color("#00FFFF")).
44 | Foreground(lipgloss.Color("0"))
45 | logger.SetStyles(styles)
46 |
47 | // Checking public directory existence
48 | if err := os.MkdirAll(publicDir, 0755); err != nil {
49 | logger.Fatal("Failed to create public directory", "error", err)
50 | }
51 |
52 | // Clear the terminal
53 | fmt.Print("\033[H\033[2J")
54 |
55 | // Generate HTML for each route
56 | for _, route := range routes {
57 | if err := generateHTML(route); err != nil {
58 | logger.Fatal("Failed to generate HTML", "route", route, "error", err)
59 | }
60 | }
61 |
62 | // Clean up obsolete files
63 | if err := cleanUp(); err != nil {
64 | logger.Fatal("Failed to clean up obsolete files", "error", err)
65 | }
66 | }
67 |
68 | func generateHTML(route string) error {
69 | filename := "index.html"
70 | if route != "/" {
71 | filename = route[1:] + ".html"
72 | }
73 | filePath := filepath.Join(publicDir, filename)
74 |
75 | file, err := os.Create(filePath)
76 | if err != nil {
77 | return fmt.Errorf("failed to create file %s: %w", filePath, err)
78 | }
79 | defer file.Close()
80 |
81 | if err := components.App(route).Render(context.Background(), file); err != nil {
82 | return fmt.Errorf("failed to render HTML for route %s: %w", route, err)
83 | }
84 |
85 | logger.Infof("Successfully generated file %s", gopherStyle.Render(filePath))
86 | return nil
87 | }
88 |
89 | func routeExists(slice []string, item string) bool {
90 | for _, s := range slice {
91 | if s == item {
92 | return true
93 | }
94 | }
95 | return false
96 | }
97 |
98 | func cleanUp() error {
99 | files, err := os.ReadDir(publicDir)
100 | if err != nil {
101 | return fmt.Errorf("error reading directory: %w", err)
102 | }
103 |
104 | for _, file := range files {
105 | if file.IsDir() {
106 | continue // Skip directories
107 | }
108 | filename := file.Name()
109 | route := "/" + strings.TrimSuffix(filename, ".html")
110 | if filename == "index.html" {
111 | route = "/"
112 | }
113 | if !routeExists(routes, route) {
114 | filePath := filepath.Join(publicDir, filename)
115 | if err := os.Remove(filePath); err != nil {
116 | logger.Error("Failed to remove file", "file", filePath, "error", err)
117 | } else {
118 | logger.Info("Removed obsolete file", "file", filePath)
119 | }
120 | }
121 | }
122 | return nil
123 | }
124 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "tailwindcss": "^3.4.9",
4 | "vite": "^5.4.0"
5 | },
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/public/contact.html:
--------------------------------------------------------------------------------
1 | GOAT StackContact Us
Have questions or need support?
Send an EmailOr email us directly at:
support@morethancoder.com
--------------------------------------------------------------------------------
/public/examples.html:
--------------------------------------------------------------------------------
1 | GOAT StackGOAT Stack Examples
Explore the power of Go templates with Alpine.js and Tailwind CSS
Button Component
templ Button(text string, classes string) {
8 | <button class={ "px-4 py-2 rounded hover:opacity-80 transition duration-300 " + classes }>{ text }</button>
9 | }
10 |
11 | // Usage in another template:
12 | @Button("Primary", "bg-blue-500 text-white")
13 | @Button("Secondary", "bg-gray-200 text-gray-800")
14 | @Button("Outline", "border border-gray-300 text-gray-700")
Toggle Switch
templ ToggleSwitch(label string) {
26 | <div x-data="{ isOn: false }" class="flex items-center space-x-4">
27 | <span class="text-sm font-medium text-gray-900 dark:text-gray-300">{ label }</span>
28 | <button
29 | @click="isOn = !isOn"
30 | :class="isOn ? 'bg-blue-600' : 'bg-gray-200'"
31 | class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
32 | >
33 | <span
34 | :class="isOn ? 'translate-x-5' : 'translate-x-0'"
35 | class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
36 | ></span>
37 | </button>
38 | <span x-text="isOn ? 'ON' : 'OFF'" class="text-sm font-medium text-gray-900 dark:text-gray-300"></span>
39 | </div>
40 | }
41 |
42 | // Usage in another template:
43 | @ToggleSwitch("Toggle Me")
Counter
Count:
templ Counter() {
63 | <div x-data="{ count: 0 }" class="text-center">
64 | <p class="text-2xl font-bold mb-4">Count: <span x-text="count"></span></p>
65 | <div class="space-x-2">
66 | <button @click="count++" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 transition duration-300">Increment</button>
67 | <button @click="count--" class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600 transition duration-300">Decrement</button>
68 | </div>
69 | </div>
70 | }
71 |
72 | // Usage in another template:
73 | @Counter()
Modal Dialog
Modal Title
This is the modal content.
templ Modal(buttonText, title, content string) {
86 | <div x-data="{ isOpen: false }">
87 | <button @click="isOpen = true" class="bg-purple-500 text-white px-4 py-2 rounded hover:bg-purple-600 transition duration-300">{ buttonText }</button>
88 |
89 | <div x-show="isOpen" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" style="display: none;">
90 | <div class="bg-white p-6 rounded-lg shadow-lg">
91 | <h3 class="text-lg font-semibold mb-2">{ title }</h3>
92 | <p class="mb-4">{ content }</p>
93 | <button @click="isOpen = false" class="bg-gray-300 text-gray-800 px-4 py-2 rounded hover:bg-gray-400 transition duration-300">Close</button>
94 | </div>
95 | </div>
96 | </div>
97 | }
98 |
99 | // Usage in another template:
100 | @Modal("Open Modal", "Modal Title", "This is the modal content.")
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 | GOAT StackGOAT Stack
Blazing fast web development with Go, Alpine.js, and Tailwind CSS
Tech Stack
Powerful backend for server-side rendering and rapid static page generation.
Lightweight JavaScript framework for smooth interactivity.
Utility-first CSS framework for rapid UI development.
Next-generation frontend tooling for fast hot reloading.
Get Started
Clone the repo:
git clone https://github.com/morethancoder/goat.git yourprojectname
Change into the project directory:
Initialize the project:
go mod init yourprojectname && go mod tidy
Change the import in main.go:
// Change this:
19 | "morethancoder/goat/components"
20 | // To:
21 | "yourprojectname/components"
Install required Node modules:
Start the Vite dev server:
Start file watching & compiling:
Star the repo!
If you find this project helpful, please consider giving it a star on GitHub.
--------------------------------------------------------------------------------
/public/static/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/morethancoder/goat/416fc3c35c770efefd1b3945304a6f4def17f1f8/public/static/assets/logo.png
--------------------------------------------------------------------------------
/public/static/css/styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | ! tailwindcss v3.4.9 | MIT License | https://tailwindcss.com
3 | */
4 |
5 | /*
6 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
7 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
8 | */
9 |
10 | *,
11 | ::before,
12 | ::after {
13 | box-sizing: border-box;
14 | /* 1 */
15 | border-width: 0;
16 | /* 2 */
17 | border-style: solid;
18 | /* 2 */
19 | border-color: #e5e7eb;
20 | /* 2 */
21 | }
22 |
23 | ::before,
24 | ::after {
25 | --tw-content: '';
26 | }
27 |
28 | /*
29 | 1. Use a consistent sensible line-height in all browsers.
30 | 2. Prevent adjustments of font size after orientation changes in iOS.
31 | 3. Use a more readable tab size.
32 | 4. Use the user's configured `sans` font-family by default.
33 | 5. Use the user's configured `sans` font-feature-settings by default.
34 | 6. Use the user's configured `sans` font-variation-settings by default.
35 | 7. Disable tap highlights on iOS
36 | */
37 |
38 | html,
39 | :host {
40 | line-height: 1.5;
41 | /* 1 */
42 | -webkit-text-size-adjust: 100%;
43 | /* 2 */
44 | -moz-tab-size: 4;
45 | /* 3 */
46 | -o-tab-size: 4;
47 | tab-size: 4;
48 | /* 3 */
49 | font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
50 | /* 4 */
51 | font-feature-settings: normal;
52 | /* 5 */
53 | font-variation-settings: normal;
54 | /* 6 */
55 | -webkit-tap-highlight-color: transparent;
56 | /* 7 */
57 | }
58 |
59 | /*
60 | 1. Remove the margin in all browsers.
61 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
62 | */
63 |
64 | body {
65 | margin: 0;
66 | /* 1 */
67 | line-height: inherit;
68 | /* 2 */
69 | }
70 |
71 | /*
72 | 1. Add the correct height in Firefox.
73 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
74 | 3. Ensure horizontal rules are visible by default.
75 | */
76 |
77 | hr {
78 | height: 0;
79 | /* 1 */
80 | color: inherit;
81 | /* 2 */
82 | border-top-width: 1px;
83 | /* 3 */
84 | }
85 |
86 | /*
87 | Add the correct text decoration in Chrome, Edge, and Safari.
88 | */
89 |
90 | abbr:where([title]) {
91 | -webkit-text-decoration: underline dotted;
92 | text-decoration: underline dotted;
93 | }
94 |
95 | /*
96 | Remove the default font size and weight for headings.
97 | */
98 |
99 | h1,
100 | h2,
101 | h3,
102 | h4,
103 | h5,
104 | h6 {
105 | font-size: inherit;
106 | font-weight: inherit;
107 | }
108 |
109 | /*
110 | Reset links to optimize for opt-in styling instead of opt-out.
111 | */
112 |
113 | a {
114 | color: inherit;
115 | text-decoration: inherit;
116 | }
117 |
118 | /*
119 | Add the correct font weight in Edge and Safari.
120 | */
121 |
122 | b,
123 | strong {
124 | font-weight: bolder;
125 | }
126 |
127 | /*
128 | 1. Use the user's configured `mono` font-family by default.
129 | 2. Use the user's configured `mono` font-feature-settings by default.
130 | 3. Use the user's configured `mono` font-variation-settings by default.
131 | 4. Correct the odd `em` font sizing in all browsers.
132 | */
133 |
134 | code,
135 | kbd,
136 | samp,
137 | pre {
138 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
139 | /* 1 */
140 | font-feature-settings: normal;
141 | /* 2 */
142 | font-variation-settings: normal;
143 | /* 3 */
144 | font-size: 1em;
145 | /* 4 */
146 | }
147 |
148 | /*
149 | Add the correct font size in all browsers.
150 | */
151 |
152 | small {
153 | font-size: 80%;
154 | }
155 |
156 | /*
157 | Prevent `sub` and `sup` elements from affecting the line height in all browsers.
158 | */
159 |
160 | sub,
161 | sup {
162 | font-size: 75%;
163 | line-height: 0;
164 | position: relative;
165 | vertical-align: baseline;
166 | }
167 |
168 | sub {
169 | bottom: -0.25em;
170 | }
171 |
172 | sup {
173 | top: -0.5em;
174 | }
175 |
176 | /*
177 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
178 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
179 | 3. Remove gaps between table borders by default.
180 | */
181 |
182 | table {
183 | text-indent: 0;
184 | /* 1 */
185 | border-color: inherit;
186 | /* 2 */
187 | border-collapse: collapse;
188 | /* 3 */
189 | }
190 |
191 | /*
192 | 1. Change the font styles in all browsers.
193 | 2. Remove the margin in Firefox and Safari.
194 | 3. Remove default padding in all browsers.
195 | */
196 |
197 | button,
198 | input,
199 | optgroup,
200 | select,
201 | textarea {
202 | font-family: inherit;
203 | /* 1 */
204 | font-feature-settings: inherit;
205 | /* 1 */
206 | font-variation-settings: inherit;
207 | /* 1 */
208 | font-size: 100%;
209 | /* 1 */
210 | font-weight: inherit;
211 | /* 1 */
212 | line-height: inherit;
213 | /* 1 */
214 | letter-spacing: inherit;
215 | /* 1 */
216 | color: inherit;
217 | /* 1 */
218 | margin: 0;
219 | /* 2 */
220 | padding: 0;
221 | /* 3 */
222 | }
223 |
224 | /*
225 | Remove the inheritance of text transform in Edge and Firefox.
226 | */
227 |
228 | button,
229 | select {
230 | text-transform: none;
231 | }
232 |
233 | /*
234 | 1. Correct the inability to style clickable types in iOS and Safari.
235 | 2. Remove default button styles.
236 | */
237 |
238 | button,
239 | input:where([type='button']),
240 | input:where([type='reset']),
241 | input:where([type='submit']) {
242 | -webkit-appearance: button;
243 | /* 1 */
244 | background-color: transparent;
245 | /* 2 */
246 | background-image: none;
247 | /* 2 */
248 | }
249 |
250 | /*
251 | Use the modern Firefox focus style for all focusable elements.
252 | */
253 |
254 | :-moz-focusring {
255 | outline: auto;
256 | }
257 |
258 | /*
259 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
260 | */
261 |
262 | :-moz-ui-invalid {
263 | box-shadow: none;
264 | }
265 |
266 | /*
267 | Add the correct vertical alignment in Chrome and Firefox.
268 | */
269 |
270 | progress {
271 | vertical-align: baseline;
272 | }
273 |
274 | /*
275 | Correct the cursor style of increment and decrement buttons in Safari.
276 | */
277 |
278 | ::-webkit-inner-spin-button,
279 | ::-webkit-outer-spin-button {
280 | height: auto;
281 | }
282 |
283 | /*
284 | 1. Correct the odd appearance in Chrome and Safari.
285 | 2. Correct the outline style in Safari.
286 | */
287 |
288 | [type='search'] {
289 | -webkit-appearance: textfield;
290 | /* 1 */
291 | outline-offset: -2px;
292 | /* 2 */
293 | }
294 |
295 | /*
296 | Remove the inner padding in Chrome and Safari on macOS.
297 | */
298 |
299 | ::-webkit-search-decoration {
300 | -webkit-appearance: none;
301 | }
302 |
303 | /*
304 | 1. Correct the inability to style clickable types in iOS and Safari.
305 | 2. Change font properties to `inherit` in Safari.
306 | */
307 |
308 | ::-webkit-file-upload-button {
309 | -webkit-appearance: button;
310 | /* 1 */
311 | font: inherit;
312 | /* 2 */
313 | }
314 |
315 | /*
316 | Add the correct display in Chrome and Safari.
317 | */
318 |
319 | summary {
320 | display: list-item;
321 | }
322 |
323 | /*
324 | Removes the default spacing and border for appropriate elements.
325 | */
326 |
327 | blockquote,
328 | dl,
329 | dd,
330 | h1,
331 | h2,
332 | h3,
333 | h4,
334 | h5,
335 | h6,
336 | hr,
337 | figure,
338 | p,
339 | pre {
340 | margin: 0;
341 | }
342 |
343 | fieldset {
344 | margin: 0;
345 | padding: 0;
346 | }
347 |
348 | legend {
349 | padding: 0;
350 | }
351 |
352 | ol,
353 | ul,
354 | menu {
355 | list-style: none;
356 | margin: 0;
357 | padding: 0;
358 | }
359 |
360 | /*
361 | Reset default styling for dialogs.
362 | */
363 |
364 | dialog {
365 | padding: 0;
366 | }
367 |
368 | /*
369 | Prevent resizing textareas horizontally by default.
370 | */
371 |
372 | textarea {
373 | resize: vertical;
374 | }
375 |
376 | /*
377 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
378 | 2. Set the default placeholder color to the user's configured gray 400 color.
379 | */
380 |
381 | input::-moz-placeholder, textarea::-moz-placeholder {
382 | opacity: 1;
383 | /* 1 */
384 | color: #9ca3af;
385 | /* 2 */
386 | }
387 |
388 | input::placeholder,
389 | textarea::placeholder {
390 | opacity: 1;
391 | /* 1 */
392 | color: #9ca3af;
393 | /* 2 */
394 | }
395 |
396 | /*
397 | Set the default cursor for buttons.
398 | */
399 |
400 | button,
401 | [role="button"] {
402 | cursor: pointer;
403 | }
404 |
405 | /*
406 | Make sure disabled buttons don't get the pointer cursor.
407 | */
408 |
409 | :disabled {
410 | cursor: default;
411 | }
412 |
413 | /*
414 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
415 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
416 | This can trigger a poorly considered lint error in some tools but is included by design.
417 | */
418 |
419 | img,
420 | svg,
421 | video,
422 | canvas,
423 | audio,
424 | iframe,
425 | embed,
426 | object {
427 | display: block;
428 | /* 1 */
429 | vertical-align: middle;
430 | /* 2 */
431 | }
432 |
433 | /*
434 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
435 | */
436 |
437 | img,
438 | video {
439 | max-width: 100%;
440 | height: auto;
441 | }
442 |
443 | /* Make elements with the HTML hidden attribute stay hidden by default */
444 |
445 | [hidden] {
446 | display: none;
447 | }
448 |
449 | *, ::before, ::after {
450 | --tw-border-spacing-x: 0;
451 | --tw-border-spacing-y: 0;
452 | --tw-translate-x: 0;
453 | --tw-translate-y: 0;
454 | --tw-rotate: 0;
455 | --tw-skew-x: 0;
456 | --tw-skew-y: 0;
457 | --tw-scale-x: 1;
458 | --tw-scale-y: 1;
459 | --tw-pan-x: ;
460 | --tw-pan-y: ;
461 | --tw-pinch-zoom: ;
462 | --tw-scroll-snap-strictness: proximity;
463 | --tw-gradient-from-position: ;
464 | --tw-gradient-via-position: ;
465 | --tw-gradient-to-position: ;
466 | --tw-ordinal: ;
467 | --tw-slashed-zero: ;
468 | --tw-numeric-figure: ;
469 | --tw-numeric-spacing: ;
470 | --tw-numeric-fraction: ;
471 | --tw-ring-inset: ;
472 | --tw-ring-offset-width: 0px;
473 | --tw-ring-offset-color: #fff;
474 | --tw-ring-color: rgb(59 130 246 / 0.5);
475 | --tw-ring-offset-shadow: 0 0 #0000;
476 | --tw-ring-shadow: 0 0 #0000;
477 | --tw-shadow: 0 0 #0000;
478 | --tw-shadow-colored: 0 0 #0000;
479 | --tw-blur: ;
480 | --tw-brightness: ;
481 | --tw-contrast: ;
482 | --tw-grayscale: ;
483 | --tw-hue-rotate: ;
484 | --tw-invert: ;
485 | --tw-saturate: ;
486 | --tw-sepia: ;
487 | --tw-drop-shadow: ;
488 | --tw-backdrop-blur: ;
489 | --tw-backdrop-brightness: ;
490 | --tw-backdrop-contrast: ;
491 | --tw-backdrop-grayscale: ;
492 | --tw-backdrop-hue-rotate: ;
493 | --tw-backdrop-invert: ;
494 | --tw-backdrop-opacity: ;
495 | --tw-backdrop-saturate: ;
496 | --tw-backdrop-sepia: ;
497 | --tw-contain-size: ;
498 | --tw-contain-layout: ;
499 | --tw-contain-paint: ;
500 | --tw-contain-style: ;
501 | }
502 |
503 | ::backdrop {
504 | --tw-border-spacing-x: 0;
505 | --tw-border-spacing-y: 0;
506 | --tw-translate-x: 0;
507 | --tw-translate-y: 0;
508 | --tw-rotate: 0;
509 | --tw-skew-x: 0;
510 | --tw-skew-y: 0;
511 | --tw-scale-x: 1;
512 | --tw-scale-y: 1;
513 | --tw-pan-x: ;
514 | --tw-pan-y: ;
515 | --tw-pinch-zoom: ;
516 | --tw-scroll-snap-strictness: proximity;
517 | --tw-gradient-from-position: ;
518 | --tw-gradient-via-position: ;
519 | --tw-gradient-to-position: ;
520 | --tw-ordinal: ;
521 | --tw-slashed-zero: ;
522 | --tw-numeric-figure: ;
523 | --tw-numeric-spacing: ;
524 | --tw-numeric-fraction: ;
525 | --tw-ring-inset: ;
526 | --tw-ring-offset-width: 0px;
527 | --tw-ring-offset-color: #fff;
528 | --tw-ring-color: rgb(59 130 246 / 0.5);
529 | --tw-ring-offset-shadow: 0 0 #0000;
530 | --tw-ring-shadow: 0 0 #0000;
531 | --tw-shadow: 0 0 #0000;
532 | --tw-shadow-colored: 0 0 #0000;
533 | --tw-blur: ;
534 | --tw-brightness: ;
535 | --tw-contrast: ;
536 | --tw-grayscale: ;
537 | --tw-hue-rotate: ;
538 | --tw-invert: ;
539 | --tw-saturate: ;
540 | --tw-sepia: ;
541 | --tw-drop-shadow: ;
542 | --tw-backdrop-blur: ;
543 | --tw-backdrop-brightness: ;
544 | --tw-backdrop-contrast: ;
545 | --tw-backdrop-grayscale: ;
546 | --tw-backdrop-hue-rotate: ;
547 | --tw-backdrop-invert: ;
548 | --tw-backdrop-opacity: ;
549 | --tw-backdrop-saturate: ;
550 | --tw-backdrop-sepia: ;
551 | --tw-contain-size: ;
552 | --tw-contain-layout: ;
553 | --tw-contain-paint: ;
554 | --tw-contain-style: ;
555 | }
556 |
557 | .pointer-events-none {
558 | pointer-events: none;
559 | }
560 |
561 | .static {
562 | position: static;
563 | }
564 |
565 | .fixed {
566 | position: fixed;
567 | }
568 |
569 | .absolute {
570 | position: absolute;
571 | }
572 |
573 | .relative {
574 | position: relative;
575 | }
576 |
577 | .sticky {
578 | position: sticky;
579 | }
580 |
581 | .inset-0 {
582 | inset: 0px;
583 | }
584 |
585 | .bottom-4 {
586 | bottom: 1rem;
587 | }
588 |
589 | .left-4 {
590 | left: 1rem;
591 | }
592 |
593 | .right-2 {
594 | right: 0.5rem;
595 | }
596 |
597 | .right-4 {
598 | right: 1rem;
599 | }
600 |
601 | .top-0 {
602 | top: 0px;
603 | }
604 |
605 | .top-2 {
606 | top: 0.5rem;
607 | }
608 |
609 | .top-4 {
610 | top: 1rem;
611 | }
612 |
613 | .z-10 {
614 | z-index: 10;
615 | }
616 |
617 | .mx-1 {
618 | margin-left: 0.25rem;
619 | margin-right: 0.25rem;
620 | }
621 |
622 | .mx-auto {
623 | margin-left: auto;
624 | margin-right: auto;
625 | }
626 |
627 | .mb-2 {
628 | margin-bottom: 0.5rem;
629 | }
630 |
631 | .mb-4 {
632 | margin-bottom: 1rem;
633 | }
634 |
635 | .mb-6 {
636 | margin-bottom: 1.5rem;
637 | }
638 |
639 | .mb-8 {
640 | margin-bottom: 2rem;
641 | }
642 |
643 | .ml-1 {
644 | margin-left: 0.25rem;
645 | }
646 |
647 | .ml-2 {
648 | margin-left: 0.5rem;
649 | }
650 |
651 | .mr-2 {
652 | margin-right: 0.5rem;
653 | }
654 |
655 | .mt-16 {
656 | margin-top: 4rem;
657 | }
658 |
659 | .mt-8 {
660 | margin-top: 2rem;
661 | }
662 |
663 | .block {
664 | display: block;
665 | }
666 |
667 | .inline-block {
668 | display: inline-block;
669 | }
670 |
671 | .flex {
672 | display: flex;
673 | }
674 |
675 | .inline-flex {
676 | display: inline-flex;
677 | }
678 |
679 | .grid {
680 | display: grid;
681 | }
682 |
683 | .hidden {
684 | display: none;
685 | }
686 |
687 | .size-5 {
688 | width: 1.25rem;
689 | height: 1.25rem;
690 | }
691 |
692 | .h-10 {
693 | height: 2.5rem;
694 | }
695 |
696 | .h-4 {
697 | height: 1rem;
698 | }
699 |
700 | .h-5 {
701 | height: 1.25rem;
702 | }
703 |
704 | .h-6 {
705 | height: 1.5rem;
706 | }
707 |
708 | .h-\[70svh\] {
709 | height: 70svh;
710 | }
711 |
712 | .min-h-screen {
713 | min-height: 100vh;
714 | }
715 |
716 | .w-10 {
717 | width: 2.5rem;
718 | }
719 |
720 | .w-11 {
721 | width: 2.75rem;
722 | }
723 |
724 | .w-4 {
725 | width: 1rem;
726 | }
727 |
728 | .w-5 {
729 | width: 1.25rem;
730 | }
731 |
732 | .w-6 {
733 | width: 1.5rem;
734 | }
735 |
736 | .w-full {
737 | width: 100%;
738 | }
739 |
740 | .max-w-2xl {
741 | max-width: 42rem;
742 | }
743 |
744 | .max-w-4xl {
745 | max-width: 56rem;
746 | }
747 |
748 | .max-w-md {
749 | max-width: 28rem;
750 | }
751 |
752 | .max-w-xl {
753 | max-width: 36rem;
754 | }
755 |
756 | .max-w-xs {
757 | max-width: 20rem;
758 | }
759 |
760 | .flex-shrink-0 {
761 | flex-shrink: 0;
762 | }
763 |
764 | .flex-grow {
765 | flex-grow: 1;
766 | }
767 |
768 | .translate-x-0 {
769 | --tw-translate-x: 0px;
770 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
771 | }
772 |
773 | .translate-x-5 {
774 | --tw-translate-x: 1.25rem;
775 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
776 | }
777 |
778 | .translate-y-0 {
779 | --tw-translate-y: 0px;
780 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
781 | }
782 |
783 | .translate-y-2 {
784 | --tw-translate-y: 0.5rem;
785 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
786 | }
787 |
788 | .transform {
789 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
790 | }
791 |
792 | .cursor-pointer {
793 | cursor: pointer;
794 | }
795 |
796 | .select-none {
797 | -webkit-user-select: none;
798 | -moz-user-select: none;
799 | user-select: none;
800 | }
801 |
802 | .list-none {
803 | list-style-type: none;
804 | }
805 |
806 | .grid-cols-1 {
807 | grid-template-columns: repeat(1, minmax(0, 1fr));
808 | }
809 |
810 | .flex-col {
811 | flex-direction: column;
812 | }
813 |
814 | .items-end {
815 | align-items: flex-end;
816 | }
817 |
818 | .items-center {
819 | align-items: center;
820 | }
821 |
822 | .justify-start {
823 | justify-content: flex-start;
824 | }
825 |
826 | .justify-center {
827 | justify-content: center;
828 | }
829 |
830 | .justify-between {
831 | justify-content: space-between;
832 | }
833 |
834 | .gap-6 {
835 | gap: 1.5rem;
836 | }
837 |
838 | .space-x-1 > :not([hidden]) ~ :not([hidden]) {
839 | --tw-space-x-reverse: 0;
840 | margin-right: calc(0.25rem * var(--tw-space-x-reverse));
841 | margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse)));
842 | }
843 |
844 | .space-x-2 > :not([hidden]) ~ :not([hidden]) {
845 | --tw-space-x-reverse: 0;
846 | margin-right: calc(0.5rem * var(--tw-space-x-reverse));
847 | margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
848 | }
849 |
850 | .space-x-4 > :not([hidden]) ~ :not([hidden]) {
851 | --tw-space-x-reverse: 0;
852 | margin-right: calc(1rem * var(--tw-space-x-reverse));
853 | margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
854 | }
855 |
856 | .space-y-12 > :not([hidden]) ~ :not([hidden]) {
857 | --tw-space-y-reverse: 0;
858 | margin-top: calc(3rem * calc(1 - var(--tw-space-y-reverse)));
859 | margin-bottom: calc(3rem * var(--tw-space-y-reverse));
860 | }
861 |
862 | .space-y-2 > :not([hidden]) ~ :not([hidden]) {
863 | --tw-space-y-reverse: 0;
864 | margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
865 | margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
866 | }
867 |
868 | .space-y-6 > :not([hidden]) ~ :not([hidden]) {
869 | --tw-space-y-reverse: 0;
870 | margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
871 | margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
872 | }
873 |
874 | .space-y-8 > :not([hidden]) ~ :not([hidden]) {
875 | --tw-space-y-reverse: 0;
876 | margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
877 | margin-bottom: calc(2rem * var(--tw-space-y-reverse));
878 | }
879 |
880 | .overflow-hidden {
881 | overflow: hidden;
882 | }
883 |
884 | .overflow-x-auto {
885 | overflow-x: auto;
886 | }
887 |
888 | .rounded {
889 | border-radius: 0.25rem;
890 | }
891 |
892 | .rounded-full {
893 | border-radius: 9999px;
894 | }
895 |
896 | .rounded-lg {
897 | border-radius: 0.5rem;
898 | }
899 |
900 | .rounded-md {
901 | border-radius: 0.375rem;
902 | }
903 |
904 | .border {
905 | border-width: 1px;
906 | }
907 |
908 | .border-2 {
909 | border-width: 2px;
910 | }
911 |
912 | .border-current {
913 | border-color: currentColor;
914 | }
915 |
916 | .border-gray-300 {
917 | --tw-border-opacity: 1;
918 | border-color: rgb(209 213 219 / var(--tw-border-opacity));
919 | }
920 |
921 | .border-stone-800 {
922 | --tw-border-opacity: 1;
923 | border-color: rgb(41 37 36 / var(--tw-border-opacity));
924 | }
925 |
926 | .border-transparent {
927 | border-color: transparent;
928 | }
929 |
930 | .bg-black {
931 | --tw-bg-opacity: 1;
932 | background-color: rgb(0 0 0 / var(--tw-bg-opacity));
933 | }
934 |
935 | .bg-blue-500 {
936 | --tw-bg-opacity: 1;
937 | background-color: rgb(59 130 246 / var(--tw-bg-opacity));
938 | }
939 |
940 | .bg-blue-600 {
941 | --tw-bg-opacity: 1;
942 | background-color: rgb(37 99 235 / var(--tw-bg-opacity));
943 | }
944 |
945 | .bg-gray-200 {
946 | --tw-bg-opacity: 1;
947 | background-color: rgb(229 231 235 / var(--tw-bg-opacity));
948 | }
949 |
950 | .bg-gray-300 {
951 | --tw-bg-opacity: 1;
952 | background-color: rgb(209 213 219 / var(--tw-bg-opacity));
953 | }
954 |
955 | .bg-green-500 {
956 | --tw-bg-opacity: 1;
957 | background-color: rgb(34 197 94 / var(--tw-bg-opacity));
958 | }
959 |
960 | .bg-purple-500 {
961 | --tw-bg-opacity: 1;
962 | background-color: rgb(168 85 247 / var(--tw-bg-opacity));
963 | }
964 |
965 | .bg-red-500 {
966 | --tw-bg-opacity: 1;
967 | background-color: rgb(239 68 68 / var(--tw-bg-opacity));
968 | }
969 |
970 | .bg-stone-100 {
971 | --tw-bg-opacity: 1;
972 | background-color: rgb(245 245 244 / var(--tw-bg-opacity));
973 | }
974 |
975 | .bg-stone-200 {
976 | --tw-bg-opacity: 1;
977 | background-color: rgb(231 229 228 / var(--tw-bg-opacity));
978 | }
979 |
980 | .bg-stone-50 {
981 | --tw-bg-opacity: 1;
982 | background-color: rgb(250 250 249 / var(--tw-bg-opacity));
983 | }
984 |
985 | .bg-stone-800 {
986 | --tw-bg-opacity: 1;
987 | background-color: rgb(41 37 36 / var(--tw-bg-opacity));
988 | }
989 |
990 | .bg-white {
991 | --tw-bg-opacity: 1;
992 | background-color: rgb(255 255 255 / var(--tw-bg-opacity));
993 | }
994 |
995 | .bg-yellow-500 {
996 | --tw-bg-opacity: 1;
997 | background-color: rgb(234 179 8 / var(--tw-bg-opacity));
998 | }
999 |
1000 | .bg-opacity-50 {
1001 | --tw-bg-opacity: 0.5;
1002 | }
1003 |
1004 | .p-1 {
1005 | padding: 0.25rem;
1006 | }
1007 |
1008 | .p-2 {
1009 | padding: 0.5rem;
1010 | }
1011 |
1012 | .p-4 {
1013 | padding: 1rem;
1014 | }
1015 |
1016 | .p-6 {
1017 | padding: 1.5rem;
1018 | }
1019 |
1020 | .p-8 {
1021 | padding: 2rem;
1022 | }
1023 |
1024 | .px-2 {
1025 | padding-left: 0.5rem;
1026 | padding-right: 0.5rem;
1027 | }
1028 |
1029 | .px-3 {
1030 | padding-left: 0.75rem;
1031 | padding-right: 0.75rem;
1032 | }
1033 |
1034 | .px-4 {
1035 | padding-left: 1rem;
1036 | padding-right: 1rem;
1037 | }
1038 |
1039 | .py-1 {
1040 | padding-top: 0.25rem;
1041 | padding-bottom: 0.25rem;
1042 | }
1043 |
1044 | .py-2 {
1045 | padding-top: 0.5rem;
1046 | padding-bottom: 0.5rem;
1047 | }
1048 |
1049 | .py-3 {
1050 | padding-top: 0.75rem;
1051 | padding-bottom: 0.75rem;
1052 | }
1053 |
1054 | .text-center {
1055 | text-align: center;
1056 | }
1057 |
1058 | .font-mono {
1059 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1060 | }
1061 |
1062 | .text-2xl {
1063 | font-size: 1.5rem;
1064 | line-height: 2rem;
1065 | }
1066 |
1067 | .text-3xl {
1068 | font-size: 1.875rem;
1069 | line-height: 2.25rem;
1070 | }
1071 |
1072 | .text-4xl {
1073 | font-size: 2.25rem;
1074 | line-height: 2.5rem;
1075 | }
1076 |
1077 | .text-lg {
1078 | font-size: 1.125rem;
1079 | line-height: 1.75rem;
1080 | }
1081 |
1082 | .text-sm {
1083 | font-size: 0.875rem;
1084 | line-height: 1.25rem;
1085 | }
1086 |
1087 | .text-xl {
1088 | font-size: 1.25rem;
1089 | line-height: 1.75rem;
1090 | }
1091 |
1092 | .text-xs {
1093 | font-size: 0.75rem;
1094 | line-height: 1rem;
1095 | }
1096 |
1097 | .font-bold {
1098 | font-weight: 700;
1099 | }
1100 |
1101 | .font-medium {
1102 | font-weight: 500;
1103 | }
1104 |
1105 | .font-semibold {
1106 | font-weight: 600;
1107 | }
1108 |
1109 | .text-black {
1110 | --tw-text-opacity: 1;
1111 | color: rgb(0 0 0 / var(--tw-text-opacity));
1112 | }
1113 |
1114 | .text-gray-700 {
1115 | --tw-text-opacity: 1;
1116 | color: rgb(55 65 81 / var(--tw-text-opacity));
1117 | }
1118 |
1119 | .text-gray-800 {
1120 | --tw-text-opacity: 1;
1121 | color: rgb(31 41 55 / var(--tw-text-opacity));
1122 | }
1123 |
1124 | .text-gray-900 {
1125 | --tw-text-opacity: 1;
1126 | color: rgb(17 24 39 / var(--tw-text-opacity));
1127 | }
1128 |
1129 | .text-green-500 {
1130 | --tw-text-opacity: 1;
1131 | color: rgb(34 197 94 / var(--tw-text-opacity));
1132 | }
1133 |
1134 | .text-red-500 {
1135 | --tw-text-opacity: 1;
1136 | color: rgb(239 68 68 / var(--tw-text-opacity));
1137 | }
1138 |
1139 | .text-stone-800 {
1140 | --tw-text-opacity: 1;
1141 | color: rgb(41 37 36 / var(--tw-text-opacity));
1142 | }
1143 |
1144 | .text-stone-900 {
1145 | --tw-text-opacity: 1;
1146 | color: rgb(28 25 23 / var(--tw-text-opacity));
1147 | }
1148 |
1149 | .text-white {
1150 | --tw-text-opacity: 1;
1151 | color: rgb(255 255 255 / var(--tw-text-opacity));
1152 | }
1153 |
1154 | .text-yellow-500 {
1155 | --tw-text-opacity: 1;
1156 | color: rgb(234 179 8 / var(--tw-text-opacity));
1157 | }
1158 |
1159 | .underline {
1160 | text-decoration-line: underline;
1161 | }
1162 |
1163 | .opacity-0 {
1164 | opacity: 0;
1165 | }
1166 |
1167 | .opacity-100 {
1168 | opacity: 1;
1169 | }
1170 |
1171 | .opacity-90 {
1172 | opacity: 0.9;
1173 | }
1174 |
1175 | .shadow {
1176 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
1177 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
1178 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1179 | }
1180 |
1181 | .shadow-lg {
1182 | --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
1183 | --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
1184 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1185 | }
1186 |
1187 | .shadow-md {
1188 | --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
1189 | --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
1190 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1191 | }
1192 |
1193 | .shadow-sm {
1194 | --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
1195 | --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
1196 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1197 | }
1198 |
1199 | .ring-0 {
1200 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1201 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1202 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1203 | }
1204 |
1205 | .transition {
1206 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
1207 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
1208 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
1209 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1210 | transition-duration: 150ms;
1211 | }
1212 |
1213 | .transition-all {
1214 | transition-property: all;
1215 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1216 | transition-duration: 150ms;
1217 | }
1218 |
1219 | .transition-colors {
1220 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
1221 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1222 | transition-duration: 150ms;
1223 | }
1224 |
1225 | .transition-opacity {
1226 | transition-property: opacity;
1227 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1228 | transition-duration: 150ms;
1229 | }
1230 |
1231 | .duration-200 {
1232 | transition-duration: 200ms;
1233 | }
1234 |
1235 | .duration-300 {
1236 | transition-duration: 300ms;
1237 | }
1238 |
1239 | .ease-in {
1240 | transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
1241 | }
1242 |
1243 | .ease-in-out {
1244 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1245 | }
1246 |
1247 | .ease-out {
1248 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
1249 | }
1250 |
1251 | .hover\:cursor-pointer:hover {
1252 | cursor: pointer;
1253 | }
1254 |
1255 | .hover\:bg-gray-400:hover {
1256 | --tw-bg-opacity: 1;
1257 | background-color: rgb(156 163 175 / var(--tw-bg-opacity));
1258 | }
1259 |
1260 | .hover\:bg-green-600:hover {
1261 | --tw-bg-opacity: 1;
1262 | background-color: rgb(22 163 74 / var(--tw-bg-opacity));
1263 | }
1264 |
1265 | .hover\:bg-purple-600:hover {
1266 | --tw-bg-opacity: 1;
1267 | background-color: rgb(147 51 234 / var(--tw-bg-opacity));
1268 | }
1269 |
1270 | .hover\:bg-red-600:hover {
1271 | --tw-bg-opacity: 1;
1272 | background-color: rgb(220 38 38 / var(--tw-bg-opacity));
1273 | }
1274 |
1275 | .hover\:bg-stone-100:hover {
1276 | --tw-bg-opacity: 1;
1277 | background-color: rgb(245 245 244 / var(--tw-bg-opacity));
1278 | }
1279 |
1280 | .hover\:bg-yellow-400:hover {
1281 | --tw-bg-opacity: 1;
1282 | background-color: rgb(250 204 21 / var(--tw-bg-opacity));
1283 | }
1284 |
1285 | .hover\:bg-yellow-600:hover {
1286 | --tw-bg-opacity: 1;
1287 | background-color: rgb(202 138 4 / var(--tw-bg-opacity));
1288 | }
1289 |
1290 | .hover\:text-yellow-500:hover {
1291 | --tw-text-opacity: 1;
1292 | color: rgb(234 179 8 / var(--tw-text-opacity));
1293 | }
1294 |
1295 | .hover\:decoration-yellow-500:hover {
1296 | text-decoration-color: #eab308;
1297 | }
1298 |
1299 | .hover\:opacity-80:hover {
1300 | opacity: 0.8;
1301 | }
1302 |
1303 | .focus\:outline-none:focus {
1304 | outline: 2px solid transparent;
1305 | outline-offset: 2px;
1306 | }
1307 |
1308 | .focus\:ring-2:focus {
1309 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1310 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1311 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1312 | }
1313 |
1314 | .focus\:ring-blue-500:focus {
1315 | --tw-ring-opacity: 1;
1316 | --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
1317 | }
1318 |
1319 | .focus\:ring-yellow-500:focus {
1320 | --tw-ring-opacity: 1;
1321 | --tw-ring-color: rgb(234 179 8 / var(--tw-ring-opacity));
1322 | }
1323 |
1324 | .focus\:ring-offset-2:focus {
1325 | --tw-ring-offset-width: 2px;
1326 | }
1327 |
1328 | .active\:scale-95:active {
1329 | --tw-scale-x: .95;
1330 | --tw-scale-y: .95;
1331 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1332 | }
1333 |
1334 | .group:hover .group-hover\:opacity-100 {
1335 | opacity: 1;
1336 | }
1337 |
1338 | @media (min-width: 640px) {
1339 | .sm\:mb-0 {
1340 | margin-bottom: 0px;
1341 | }
1342 |
1343 | .sm\:inline {
1344 | display: inline;
1345 | }
1346 |
1347 | .sm\:flex-row {
1348 | flex-direction: row;
1349 | }
1350 |
1351 | .sm\:items-start {
1352 | align-items: flex-start;
1353 | }
1354 |
1355 | .sm\:border-0 {
1356 | border-width: 0px;
1357 | }
1358 |
1359 | .sm\:bg-stone-100 {
1360 | --tw-bg-opacity: 1;
1361 | background-color: rgb(245 245 244 / var(--tw-bg-opacity));
1362 | }
1363 |
1364 | .sm\:p-8 {
1365 | padding: 2rem;
1366 | }
1367 |
1368 | .sm\:px-3 {
1369 | padding-left: 0.75rem;
1370 | padding-right: 0.75rem;
1371 | }
1372 |
1373 | .sm\:py-1 {
1374 | padding-top: 0.25rem;
1375 | padding-bottom: 0.25rem;
1376 | }
1377 | }
1378 |
1379 | @media (min-width: 768px) {
1380 | .md\:grid-cols-2 {
1381 | grid-template-columns: repeat(2, minmax(0, 1fr));
1382 | }
1383 | }
1384 |
1385 | @media (prefers-color-scheme: dark) {
1386 | .dark\:border-stone-200 {
1387 | --tw-border-opacity: 1;
1388 | border-color: rgb(231 229 228 / var(--tw-border-opacity));
1389 | }
1390 |
1391 | .dark\:bg-black {
1392 | --tw-bg-opacity: 1;
1393 | background-color: rgb(0 0 0 / var(--tw-bg-opacity));
1394 | }
1395 |
1396 | .dark\:bg-stone-700 {
1397 | --tw-bg-opacity: 1;
1398 | background-color: rgb(68 64 60 / var(--tw-bg-opacity));
1399 | }
1400 |
1401 | .dark\:bg-stone-800 {
1402 | --tw-bg-opacity: 1;
1403 | background-color: rgb(41 37 36 / var(--tw-bg-opacity));
1404 | }
1405 |
1406 | .dark\:text-gray-300 {
1407 | --tw-text-opacity: 1;
1408 | color: rgb(209 213 219 / var(--tw-text-opacity));
1409 | }
1410 |
1411 | .dark\:text-stone-100 {
1412 | --tw-text-opacity: 1;
1413 | color: rgb(245 245 244 / var(--tw-text-opacity));
1414 | }
1415 |
1416 | .dark\:text-stone-200 {
1417 | --tw-text-opacity: 1;
1418 | color: rgb(231 229 228 / var(--tw-text-opacity));
1419 | }
1420 |
1421 | .dark\:text-stone-300 {
1422 | --tw-text-opacity: 1;
1423 | color: rgb(214 211 209 / var(--tw-text-opacity));
1424 | }
1425 |
1426 | .dark\:text-white {
1427 | --tw-text-opacity: 1;
1428 | color: rgb(255 255 255 / var(--tw-text-opacity));
1429 | }
1430 |
1431 | .dark\:shadow-none {
1432 | --tw-shadow: 0 0 #0000;
1433 | --tw-shadow-colored: 0 0 #0000;
1434 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1435 | }
1436 |
1437 | .dark\:hover\:bg-stone-800:hover {
1438 | --tw-bg-opacity: 1;
1439 | background-color: rgb(41 37 36 / var(--tw-bg-opacity));
1440 | }
1441 |
1442 | .dark\:hover\:text-yellow-400:hover {
1443 | --tw-text-opacity: 1;
1444 | color: rgb(250 204 21 / var(--tw-text-opacity));
1445 | }
1446 | }
1447 |
1448 | @media (min-width: 640px) {
1449 | @media (prefers-color-scheme: dark) {
1450 | .sm\:dark\:bg-stone-800 {
1451 | --tw-bg-opacity: 1;
1452 | background-color: rgb(41 37 36 / var(--tw-bg-opacity));
1453 | }
1454 | }
1455 | }
1456 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ["./components/*.templ"],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | }
9 |
--------------------------------------------------------------------------------
/thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/morethancoder/goat/416fc3c35c770efefd1b3945304a6f4def17f1f8/thumb.png
--------------------------------------------------------------------------------