Compare commits
No commits in common. "d9d0b39d594352c6d7f5abd5dfd3c454305f0422" and "9f741eecbe61ffc84141f2b3def7b9a0c035de9c" have entirely different histories.
d9d0b39d59
...
9f741eecbe
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
/tmp
|
||||
output.css
|
||||
config.json
|
5
main.go
5
main.go
@ -3,15 +3,16 @@ package main
|
||||
import (
|
||||
"GoStart/src/api"
|
||||
"GoStart/src/routes"
|
||||
"GoStart/src/utils"
|
||||
"GoStart/src/util"
|
||||
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
// Load Configurations
|
||||
cfg, err := utils.LoadConfig()
|
||||
cfg, err := util.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to load config: %v\n", err)
|
||||
return
|
||||
|
@ -1,7 +1,6 @@
|
||||
# GoStart
|
||||
|
||||
This is a basic starter project for building web applications using Go, Tailwind CSS, and htmx. It provides a foundation for creating interactive and responsive web applications without relying on any third-party dependencies, utilizing only the packages included with the Go language by default. This module includes a minified version of HTMX and a Custom Tailwind CSS for that add a Dark/Light themeing capability out of the box. If you would like to change the default styling of the app beyond the default tailwind components, you will need the standalone Tailwind Executable to rebuild the CSS to your liking.
|
||||
-_More information on custom styling can be found in the [Style readme](web/style/readme.md)_
|
||||
This is a basic starter project for building web applications using Go, Tailwind CSS, and htmx. It provides a foundation for creating interactive and responsive web applications without relying on any third-party dependencies, utilizing only the packages included with the Go language by default.
|
||||
|
||||
## Features
|
||||
|
||||
@ -15,7 +14,7 @@ This is a basic starter project for building web applications using Go, Tailwind
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://forge.happytavern.co/oceanslim/gostart.git
|
||||
git clone https://git.happytavern.co/oceanslim/gostart.git
|
||||
```
|
||||
|
||||
2. Navigate to the project directory:
|
||||
@ -33,7 +32,7 @@ This is a basic starter project for building web applications using Go, Tailwind
|
||||
4. Build and run the application:
|
||||
|
||||
```bash
|
||||
go run .
|
||||
go run main.go
|
||||
```
|
||||
|
||||
The application will be accessible at `http://localhost:8787`, or whatever port you set in your configuration.
|
||||
|
@ -1,15 +1,23 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"GoStart/src/utils"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ExampleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := utils.PageData{
|
||||
data := PageData{
|
||||
Title: "Example Page",
|
||||
}
|
||||
|
||||
// Call RenderTemplate with the specific template for this route
|
||||
utils.RenderTemplate(w, data, "example.html")
|
||||
tmpl, err := template.ParseFiles("web/views/#layout.html", "web/views/example.html")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = tmpl.ExecuteTemplate(w, "layout", data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
@ -1,13 +1,27 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"GoStart/src/utils"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PageData struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
func RootHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := utils.PageData{
|
||||
data := PageData{
|
||||
Title: "Home Page",
|
||||
}
|
||||
utils.RenderTemplate(w, data, "index.html")
|
||||
|
||||
tmpl, err := template.ParseFiles("web/views/#layout.html", "web/views/index.html", "web/views/#header.html", "web/views/#footer.html")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = tmpl.ExecuteTemplate(w, "layout", data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package utils
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
@ -1,10 +0,0 @@
|
||||
package utils
|
||||
|
||||
// Helper function to prepend a directory path to a list of filenames
|
||||
func PrependDir(dir string, files []string) []string {
|
||||
var fullPaths []string
|
||||
for _, file := range files {
|
||||
fullPaths = append(fullPaths, dir+file)
|
||||
}
|
||||
return fullPaths
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PageData struct {
|
||||
Title string
|
||||
Theme string
|
||||
}
|
||||
|
||||
// Define the base directories for views and templates
|
||||
const (
|
||||
viewsDir = "web/views/"
|
||||
templatesDir = "web/views/templates/"
|
||||
)
|
||||
|
||||
// Define the common layout templates filenames
|
||||
var templateFiles = []string{
|
||||
"#layout.html",
|
||||
"header.html",
|
||||
"footer.html",
|
||||
}
|
||||
|
||||
// Initialize the common templates with full paths
|
||||
var layout = PrependDir(templatesDir, templateFiles)
|
||||
|
||||
func RenderTemplate(w http.ResponseWriter, data PageData, view string) {
|
||||
|
||||
// Append the specific template for the route
|
||||
templates := append(layout, viewsDir+view)
|
||||
|
||||
// Parse all templates
|
||||
tmpl, err := template.ParseFiles(templates...)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Execute the "layout" template
|
||||
err = tmpl.ExecuteTemplate(w, "layout", data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
1
web/static/custom.min.css
vendored
1
web/static/custom.min.css
vendored
@ -1 +0,0 @@
|
||||
/*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}:root{--color-bgPrimary:#101010;--color-bgSecondary:#282828;--color-bgInverted:#e1e1e1;--color-textPrimary:#fff;--color-textSecondary:#ebebeb;--color-textMuted:#c8c8c8;--color-textInverted:#101010}:root[data-theme=light]{--color-bgPrimary:#c8c8c8;--color-bgSecondary:#e6e6e6;--color-bgInverted:#505050;--color-textPrimary:#000;--color-textSecondary:#141414;--color-textMuted:#646464;--color-textInverted:#fff}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.mb-8{margin-bottom:2rem}.mt-8{margin-top:2rem}.rounded-md{border-radius:.375rem}.bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity))}.bg-bgPrimary{background-color:var(--color-bgPrimary)}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity))}.p-2{padding:.5rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.font-bold{font-weight:700}.text-textMuted{color:var(--color-textMuted)}.text-textPrimary{color:var(--color-textPrimary)}.text-textSecondary{color:var(--color-textSecondary)}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}
|
1
web/static/htmx.min.js
vendored
Normal file
1
web/static/htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -3,24 +3,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--color-bgPrimary: rgb(16, 16, 16);
|
||||
--color-bgSecondary: rgb(40, 40, 40);
|
||||
--color-bgInverted: rgb(225, 225, 225);
|
||||
--color-textPrimary: rgb(255, 255, 255);
|
||||
--color-textSecondary: rgb(235, 235, 235);
|
||||
--color-textMuted: rgb(200, 200, 200);
|
||||
--color-textInverted: rgb(16, 16, 16);
|
||||
}
|
||||
:root[data-theme="light"] {
|
||||
--color-bgPrimary: rgb(200, 200, 200);
|
||||
--color-bgSecondary: rgb(230, 230, 230);
|
||||
--color-bgInverted: rgb(80, 80, 80);
|
||||
--color-textPrimary: rgb(0, 0, 0);
|
||||
--color-textSecondary: rgb(20, 20, 20);
|
||||
--color-textMuted: rgb(100, 100, 100);
|
||||
--color-textInverted: rgb(255, 255, 255);
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,9 @@
|
||||
# Information
|
||||
# Development
|
||||
|
||||
This repository INCLUDES a minified version of the custom css used to style the web views with themes defined in the input.css. If you want to change anything about the configuration or the input, you will need to rebuild the custom minified css by using the [Tailwind standalone CLI Tool](https://github.com/tailwindlabs/tailwindcss/releases).
|
||||
|
||||
For Tailwind to Rebuild the CSS, Tailwind must be run to compile the new styling.
|
||||
For Tailwind to Rebuild the Output CSS, a watcher must be run to compile the new styling as pages are edited.
|
||||
|
||||
To do this run:
|
||||
|
||||
```bash
|
||||
tailwindcss -i web/style/input.css -o web/static/custom.min.css --minify
|
||||
tailwindcss -i web/style/input.css -o web/static/output.css --watch
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
You can run a watcher while in development to automatically rebuild the `tailwind.min.css` whenever a file in the project directory is modified.
|
||||
|
||||
To do this run:
|
||||
|
||||
```bash
|
||||
tailwindcss -i web/style/input.css -o web/static/custom.min.css --watch --minify
|
||||
```
|
||||
|
||||
### Dark Mode
|
||||
|
||||
Yes... This framework is designed with "Dark Mode" as the default theme. As all things should be.
|
||||
|
@ -3,15 +3,7 @@ module.exports = {
|
||||
content: ["./**/*.{html,js}"],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
bgPrimary: "var(--color-bgPrimary)",
|
||||
bgSecondary: "var(--color-bgSecondary)",
|
||||
bgInverted: "var(--color-bgInverted)",
|
||||
textPrimary: "var(--color-textPrimary)",
|
||||
textSecondary: "var(--color-textSecondary)",
|
||||
textMuted: "var(--color-textMuted)",
|
||||
textInverted: "var(--color-textInverted)",
|
||||
},
|
||||
colors: {},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
5
web/views/#footer.html
Normal file
5
web/views/#footer.html
Normal file
@ -0,0 +1,5 @@
|
||||
{{define "footer"}}
|
||||
<footer>
|
||||
<p>© 2024 My Web App</p>
|
||||
</footer>
|
||||
{{end}}
|
7
web/views/#header.html
Normal file
7
web/views/#header.html
Normal file
@ -0,0 +1,7 @@
|
||||
{{define "header"}}
|
||||
<header>
|
||||
<h1 class="mt-8 mb-8 text-3xl font-bold text-blue-400">
|
||||
Welcome to My GO Web App Framework {{.Title}}
|
||||
</h1>
|
||||
</header>
|
||||
{{end}}
|
18
web/views/#layout.html
Normal file
18
web/views/#layout.html
Normal file
@ -0,0 +1,18 @@
|
||||
{{define "layout"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>GO Web App - {{.Title}}</title>
|
||||
<link rel="stylesheet" href="/static/output.css" />
|
||||
<link rel="icon" href="/static/img/favicon.ico" type="image/x-icon" />
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
</head>
|
||||
<body class="text-center text-blue-300 bg-gray-800">
|
||||
{{template "header" .}}
|
||||
{{template "view" .}}
|
||||
{{template "footer" .}}
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
@ -1,18 +1,17 @@
|
||||
{{define "view"}}
|
||||
<header>
|
||||
<h1 class="mt-8 mb-8 text-3xl font-bold text-blue-300">
|
||||
This is an additional example route
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div>You are now viewing the {{.Title}}</div>
|
||||
<h1 class="font-bold">Serve Static Files Like this Gopher</h1>
|
||||
<div class="flex justify-center mb-4">
|
||||
<img src="/static/img/gopher.png" alt="alternate text" />
|
||||
</div>
|
||||
<div>You can get back to the index view with this button</div>
|
||||
<button
|
||||
hx-get="/"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="body"
|
||||
class="p-2 text-white bg-blue-400 rounded-md"
|
||||
>
|
||||
Click Me 😀
|
||||
</button>
|
||||
<section>
|
||||
<h1 class="font-bold">Serve Static Files Like this Globe</h1>
|
||||
<img src="/static/img/gopher.png" alt="alternate text">
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© 2024 My Web App</p>
|
||||
</footer>
|
||||
{{end}}
|
||||
|
@ -1,46 +1,17 @@
|
||||
{{define "view"}}
|
||||
<main class="flex flex-col items-center justify-center p-8">
|
||||
<div class="mb-4">You are now viewing the {{.Title}}</div>
|
||||
|
||||
<div class="mb-2">
|
||||
Clicking the blue button will replace the entire page with an additional
|
||||
example view
|
||||
</div>
|
||||
<button
|
||||
hx-get="/example"
|
||||
class="p-2 mb-4 text-white bg-blue-400 rounded-md"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="body"
|
||||
>
|
||||
Click Me 😀
|
||||
<main>
|
||||
<section>
|
||||
<button hx-get="/api/example" class="p-2 text-white bg-blue-400 rounded-md">
|
||||
Click Me!
|
||||
</button>
|
||||
<h1 class="mb-2 font-bold">
|
||||
Clicking the orange button will replace the content in the box below with
|
||||
the example API
|
||||
</h1>
|
||||
<button
|
||||
hx-get="/api/example"
|
||||
hx-swap="outerHTML"
|
||||
class="p-2 mb-2 text-white bg-orange-400 rounded-md"
|
||||
hx-target="#dynamic-content"
|
||||
>
|
||||
Click Me 😀
|
||||
</button>
|
||||
<div
|
||||
class="w-full max-w-md p-4 mb-6 text-center border-2 border-white border-solid rounded-md shadow-inner bg-bgPrimary"
|
||||
>
|
||||
<!-- Content loaded via HTMX will appear here -->
|
||||
<p id="dynamic-content" class="text-textPrimary">
|
||||
Dynamic content will be displayed here.
|
||||
<h1 class="font-bold">API Example</h1>
|
||||
<a href="/api/example" target="_blank">Access Example API</a>
|
||||
<h2>Content Section</h2>
|
||||
<p>
|
||||
This is the main content of your web app. You can add any HTML content
|
||||
here.
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-2 text-center">
|
||||
<a href="/api/example" target="_blank" class="block text-blue-400"
|
||||
>You can still access traditional, json style api's</a
|
||||
>
|
||||
<a href="/example" class="block"
|
||||
>You can also create traditional route links</a
|
||||
>
|
||||
</div>
|
||||
<a href="/example">Another Example Route can be found here</a>
|
||||
</section>
|
||||
</main>
|
||||
{{end}}
|
||||
|
@ -1,34 +0,0 @@
|
||||
{{define "layout"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="{{.Theme}}">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!--
|
||||
CDNs are used for ease of development. In a Production enviornment,
|
||||
build a proper minified custom CSS using the Tailwind CLI tool and serve
|
||||
the latest available minified htmx and custom css directly from your server.
|
||||
To download a copy of htmx: https://htmx.org/docs/#download-a-copy
|
||||
|
||||
<link href="/static/tailwind.min.css" rel="stylesheet" />
|
||||
<link href="/static/htmx.min.js">
|
||||
-->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script
|
||||
src="https://unpkg.com/htmx.org@1.9.12"
|
||||
integrity="sha384-ujb1lZYygJmzgSwoxRggbCHcjc0rB2XoQrxeTUQyRjrOnlCoYta87iKBWq3EsdM2"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<!--
|
||||
link the custom minified styling included in this repo, built from the configuration
|
||||
in the /web/style directory
|
||||
-->
|
||||
<link href="/static/custom.min.css" rel="stylesheet" />
|
||||
<link rel="icon" href="/static/img/favicon.ico" type="image/x-icon" />
|
||||
<title>GO Web App - {{.Title}}</title>
|
||||
</head>
|
||||
<body class="font-mono text-center text-textPrimary bg-bgPrimary">
|
||||
{{template "header" .}} {{template "view" .}} {{template "footer" .}}
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
@ -1,5 +0,0 @@
|
||||
{{define "footer"}}
|
||||
<footer class="text-textMuted">
|
||||
<p>© 2024 My Web App</p>
|
||||
</footer>
|
||||
{{end}}
|
@ -1,7 +0,0 @@
|
||||
{{define "header"}}
|
||||
<header>
|
||||
<h1 class="mt-8 mb-8 text-3xl font-bold text-textSecondary">
|
||||
Welcome to My GO Web App Framework
|
||||
</h1>
|
||||
</header>
|
||||
{{end}}
|
Loading…
Reference in New Issue
Block a user