add:badges
This commit is contained in:
4
README.md
Normal file
4
README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Personal Site
|
||||
|
||||
Ideas:
|
||||
- dynamic metadata insertion, for ogcards
|
||||
47
components/badge.templ
Normal file
47
components/badge.templ
Normal file
@@ -0,0 +1,47 @@
|
||||
package components
|
||||
|
||||
type BadgeVariant string
|
||||
|
||||
const (
|
||||
BadgeInProgress BadgeVariant = "in-progress"
|
||||
BadgeDeployed BadgeVariant = "deployed"
|
||||
)
|
||||
|
||||
type badgeConfig struct {
|
||||
label string
|
||||
dot string
|
||||
class string
|
||||
}
|
||||
|
||||
func badgeProps(variant BadgeVariant) badgeConfig {
|
||||
switch variant {
|
||||
case BadgeDeployed:
|
||||
return badgeConfig{
|
||||
label: "Deployed",
|
||||
dot: "bg-teal-500",
|
||||
class: "bg-teal-100 text-teal-700 ring-teal-600/20",
|
||||
}
|
||||
default: // in-progress
|
||||
return badgeConfig{
|
||||
label: "In Progress",
|
||||
dot: "bg-violet-400",
|
||||
class: "bg-violet-100 text-violet-700 ring-violet-600/20",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
templ Badge(variant BadgeVariant) {
|
||||
{{ props := badgeProps(variant) }}
|
||||
<span
|
||||
class={ "flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium ring-1 ring-inset" , props.class }
|
||||
>
|
||||
if props.label == "Deployed" {
|
||||
<span class="size-3.5 text-teal-600">
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="0"><animate id="spinner_kIfO" begin="0;spinner_xBIM.end" attributeName="r" calcMode="spline" dur="1.2s" values="0;11" keySplines=".52,.6,.25,.99" fill="freeze"/><animate begin="0;spinner_xBIM.end" attributeName="opacity" calcMode="spline" dur="1.2s" values="1;0" keySplines=".52,.6,.25,.99" fill="freeze"/></circle><circle cx="12" cy="12" r="0"><animate id="spinner_Pbsh" begin="spinner_kIfO.begin+0.2s" attributeName="r" calcMode="spline" dur="1.2s" values="0;11" keySplines=".52,.6,.25,.99" fill="freeze"/><animate begin="spinner_kIfO.begin+0.2s" attributeName="opacity" calcMode="spline" dur="1.2s" values="1;0" keySplines=".52,.6,.25,.99" fill="freeze"/></circle><circle cx="12" cy="12" r="0"><animate id="spinner_xBIM" begin="spinner_kIfO.begin+0.4s" attributeName="r" calcMode="spline" dur="1.2s" values="0;11" keySplines=".52,.6,.25,.99" fill="freeze"/><animate begin="spinner_kIfO.begin+0.4s" attributeName="opacity" calcMode="spline" dur="1.2s" values="1;0" keySplines=".52,.6,.25,.99" fill="freeze"/></circle></svg>
|
||||
</span>
|
||||
} else {
|
||||
<span class={ "h-1.5 w-1.5 rounded-full animate-pulse" , props.dot }></span>
|
||||
}
|
||||
{ props.label }
|
||||
</span>
|
||||
}
|
||||
@@ -1,5 +1,21 @@
|
||||
package icons
|
||||
|
||||
templ Ban(className string) {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class={ className }
|
||||
>
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<path d="M4.929 4.929 19.07 19.071"></path>
|
||||
</svg>
|
||||
}
|
||||
|
||||
templ ArrowUpRight(className string) {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -4,43 +4,8 @@ import (
|
||||
"fmt"
|
||||
"personal-site/constants"
|
||||
"time"
|
||||
"personal-site/components/icons"
|
||||
)
|
||||
|
||||
templ navbar(params *constants.LayoutParams) {
|
||||
<header class="sticky top-0 z-50 w-full border-b border-zinc-800 bg-bg-100/80 backdrop-blur-sm">
|
||||
<div class="max-w-3xl mx-auto px-4 h-12 flex items-center justify-between">
|
||||
<a
|
||||
href="/"
|
||||
class="font-mono text-xs text-fg-200 tracking-widest uppercase hover:text-fg-100 transition-colors duration-150 shrink-0"
|
||||
>
|
||||
{ params.NavTitle }
|
||||
</a>
|
||||
<nav class="flex items-center gap-4">
|
||||
<a
|
||||
href="/projects"
|
||||
class="text-xs text-fg-300 hover:text-fg-100 transition-colors duration-150"
|
||||
>projects</a>
|
||||
<a
|
||||
href="https://git.kokopi.dev/kokopi/personal-site"
|
||||
class="text-xs text-fg-300 hover:text-fg-100 transition-colors duration-150 flex items-center gap-1.5 sm:p-0 p-0.5"
|
||||
aria-label="Gitea"
|
||||
>
|
||||
@icons.Gitea("size-4 shrink-0")
|
||||
<span class="hidden sm:inline">gitea</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/kokopi-dev/personal-site"
|
||||
class="text-xs text-fg-300 hover:text-fg-100 transition-colors duration-150 flex items-center gap-1.5 sm:p-0 p-0.5"
|
||||
aria-label="GitHub"
|
||||
>
|
||||
@icons.Github("size-4 shrink-0")
|
||||
<span class="hidden sm:inline">github</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
}
|
||||
|
||||
|
||||
templ MainLayout(params *constants.LayoutParams) {
|
||||
@@ -53,7 +18,7 @@ templ MainLayout(params *constants.LayoutParams) {
|
||||
</head>
|
||||
<body class="relative bg-linear-to-b from-bg-400 to-bg-100 text-fg-100 overflow-hidden font-sans">
|
||||
<div class="h-screen grid grid-rows-[auto_1fr_auto] overflow-y-auto overflow-x-hidden">
|
||||
@navbar(params)
|
||||
@Navbar(params)
|
||||
// base container
|
||||
<div class="p-4 mt-6 flex flex-col gap-6 max-w-3xl mx-auto">
|
||||
{ children... }
|
||||
|
||||
39
components/navbar.templ
Normal file
39
components/navbar.templ
Normal file
@@ -0,0 +1,39 @@
|
||||
package components
|
||||
|
||||
import "personal-site/constants"
|
||||
import "personal-site/components/icons"
|
||||
|
||||
templ Navbar(params *constants.LayoutParams) {
|
||||
<header class="sticky top-0 z-50 w-full border-b border-zinc-800 bg-bg-100/80 backdrop-blur-sm">
|
||||
<div class="max-w-3xl mx-auto px-4 h-12 flex items-center justify-between">
|
||||
<a
|
||||
href="/"
|
||||
class="font-mono text-xs text-fg-200 tracking-widest uppercase hover:text-fg-100 transition-colors duration-150 shrink-0"
|
||||
>
|
||||
{ params.NavTitle }
|
||||
</a>
|
||||
<nav class="flex items-center gap-4">
|
||||
<a
|
||||
href="/projects"
|
||||
class="text-xs text-fg-300 hover:text-fg-100 transition-colors duration-150"
|
||||
>projects</a>
|
||||
<a
|
||||
href="https://git.kokopi.dev/kokopi/personal-site"
|
||||
class="text-xs text-fg-300 hover:text-fg-100 transition-colors duration-150 flex items-center gap-1.5 sm:p-0 p-0.5"
|
||||
aria-label="Gitea"
|
||||
>
|
||||
@icons.Gitea("size-4 shrink-0")
|
||||
<span class="hidden sm:inline">gitea</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/kokopi-dev/personal-site"
|
||||
class="text-xs text-fg-300 hover:text-fg-100 transition-colors duration-150 flex items-center gap-1.5 sm:p-0 p-0.5"
|
||||
aria-label="GitHub"
|
||||
>
|
||||
@icons.Github("size-4 shrink-0")
|
||||
<span class="hidden sm:inline">github</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
}
|
||||
@@ -8,4 +8,38 @@ type Project struct {
|
||||
LinkGitea string
|
||||
LinkGithub string
|
||||
HostedOn string
|
||||
IsWIP bool
|
||||
}
|
||||
|
||||
var AllProjects = []Project{
|
||||
{
|
||||
Url: "https://derrickgee.dev/projects/support-ticket-demo",
|
||||
Name: "Support Ticket System",
|
||||
Description: "A sample of a robust support ticket system with the option to OAuth for database access. Authenticated users will be able to see other user created tickets. Guest mode will store tickets locally in your browser.",
|
||||
HostedOn: "Home Server",
|
||||
LinkGitea: "https://git.kokopi.dev/kokopi/personal-support-ticket-system",
|
||||
LinkGithub: "https://github.com/kokopi-dev/personal-support-ticket-system",
|
||||
TechTags: []string{"typescript", "react", "fastify", "tailwindcss", "sqlite", "vite", "bun"},
|
||||
IsWIP: false,
|
||||
},
|
||||
{
|
||||
Url: "/projects/dotfiles",
|
||||
Name: "Linux Dotfiles",
|
||||
Description: "Dotfile configurations for specific linux applications.",
|
||||
HostedOn: "",
|
||||
LinkGitea: "https://git.kokopi.dev/kokopi/dotfiles",
|
||||
LinkGithub: "https://github.com/kokopi-dev/dotfiles",
|
||||
TechTags: []string{"linux", "lua", "bash", "python"},
|
||||
IsWIP: false,
|
||||
},
|
||||
{
|
||||
Url: "/projects/dotfiles",
|
||||
Name: "Koko-chan",
|
||||
Description: "Paid discord bot service for handling schedules.",
|
||||
HostedOn: "",
|
||||
LinkGitea: "",
|
||||
LinkGithub: "",
|
||||
TechTags: []string{"golang", "templ", "tailwindcss"},
|
||||
IsWIP: true,
|
||||
},
|
||||
}
|
||||
|
||||
8
helpers/ui.go
Normal file
8
helpers/ui.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package helpers
|
||||
|
||||
func TemplIf[T any](cond bool, a, b T) T {
|
||||
if cond {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
23
main.go
23
main.go
@@ -23,28 +23,9 @@ func setupRoutesAndMiddleware() *gin.Engine {
|
||||
page := pages.Index()
|
||||
page.Render(c.Request.Context(), c.Writer)
|
||||
})
|
||||
var projects = []constants.Project{
|
||||
{
|
||||
Url: "https://derrickgee.dev/projects/support-ticket-demo",
|
||||
Name: "Support Ticket System",
|
||||
Description: "A sample of a robust support ticket system with the option to OAuth for database access. Authenticated users will be able to see other user created tickets. Guest mode will store tickets locally in your browser.",
|
||||
HostedOn: "Home Server",
|
||||
LinkGitea: "https://git.kokopi.dev/kokopi/personal-support-ticket-system",
|
||||
LinkGithub: "https://github.com/kokopi-dev/personal-support-ticket-system",
|
||||
TechTags: []string{"typescript", "react", "fastify", "tailwindcss", "sqlite", "vite", "bun"},
|
||||
},
|
||||
{
|
||||
Url: "/projects/dotfiles",
|
||||
Name: "Linux Dotfiles",
|
||||
Description: "Configurations for Linux",
|
||||
HostedOn: "",
|
||||
LinkGitea: "https://git.kokopi.dev/kokopi/dotfiles",
|
||||
LinkGithub: "https://github.com/kokopi-dev/dotfiles",
|
||||
TechTags: []string{"Linux", "Lua", "Bash", "Python"},
|
||||
},
|
||||
}
|
||||
// projects defined in constants
|
||||
r.GET("/projects", func(c *gin.Context) {
|
||||
page := pages.ProjectPage(projects)
|
||||
page := pages.ProjectPage(constants.AllProjects)
|
||||
page.Render(c.Request.Context(), c.Writer)
|
||||
})
|
||||
r.GET("/projects/dotfiles", func(c *gin.Context) {
|
||||
|
||||
@@ -4,16 +4,22 @@ import "personal-site/constants"
|
||||
import "fmt"
|
||||
import "personal-site/components"
|
||||
import "personal-site/components/icons"
|
||||
import "personal-site/helpers"
|
||||
|
||||
templ projectCard(idx int, project constants.Project) {
|
||||
<div
|
||||
class="group relative rounded-lg px-5 py-4 flex items-start justify-between gap-4 fade-up bg-[#111113] border border-zinc-800 transition-all duration-150 ease-in-out hover:border-zinc-600 hover:bg-[#18181b] hover:-translate-y-0.5"
|
||||
style={ fmt.Sprintf("animation-delay: %dms", idx*30) }
|
||||
>
|
||||
<a href={ project.Url } target="_blank" class="absolute inset-0 rounded-lg" aria-label={ project.Name }></a>
|
||||
<div class="pointer-events-none relative z-10 flex-1 min-w-0">
|
||||
<div class="group relative rounded-lg px-5 py-4 flex items-start justify-between gap-4 fade-up bg-[#111113] border border-zinc-800 transition-all duration-150 ease-in-out hover:border-zinc-600 hover:bg-[#18181b] hover:-translate-y-0.5"
|
||||
style={ fmt.Sprintf("animation-delay: %dms", idx*30) }>
|
||||
<a href={ project.Url } target="_blank" class={ helpers.TemplIf(project.IsWIP, "pointer-events-none" , "" ) + " absolute inset-0 rounded-lg" } aria-label={ project.Name }></a>
|
||||
<div class={ helpers.TemplIf(project.IsWIP, "text-fg-300", "") + " pointer-events-none relative z-10 flex-1 min-w-0"}>
|
||||
<div class="flex items-center gap-2.5 mb-1.5">
|
||||
<span class="text-sm font-medium">{ project.Name }</span>
|
||||
<span>
|
||||
if project.IsWIP {
|
||||
@components.Badge(components.BadgeInProgress)
|
||||
} else {
|
||||
@components.Badge(components.BadgeDeployed)
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-zinc-500 text-xs leading-relaxed mb-3">{ project.Description }</p>
|
||||
<div class="flex flex-wrap gap-1.5 mb-3">
|
||||
@@ -29,33 +35,31 @@ templ projectCard(idx int, project constants.Project) {
|
||||
if project.LinkGitea != "" || project.LinkGithub != "" {
|
||||
<div class="flex items-center gap-2">
|
||||
if project.LinkGitea != "" {
|
||||
<a
|
||||
href={ templ.URL(project.LinkGitea) }
|
||||
target="_blank"
|
||||
class="pointer-events-auto inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[0.6875rem] font-medium bg-zinc-800 text-fg-200 border border-zinc-700 hover:border-zinc-500 hover:text-fg-100 transition-all duration-150"
|
||||
>
|
||||
<a href={ templ.URL(project.LinkGitea) } target="_blank"
|
||||
class={ helpers.TemplIf(project.IsWIP, "pointer-events-none", "pointer-events-auto") + " inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[0.6875rem] font-medium bg-zinc-800 text-fg-200 border border-zinc-700 hover:border-zinc-500 hover:text-fg-100 transition-all duration-150"}>
|
||||
@icons.Gitea("size-4")
|
||||
Gitea
|
||||
@icons.ArrowUpRight("size-3")
|
||||
</a>
|
||||
}
|
||||
if project.LinkGithub != "" {
|
||||
<a
|
||||
href={ templ.URL(project.LinkGithub) }
|
||||
target="_blank"
|
||||
class="pointer-events-auto inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[0.6875rem] font-medium bg-zinc-800 text-fg-200 border border-zinc-700 hover:border-zinc-500 hover:text-fg-100 transition-all duration-150"
|
||||
>
|
||||
<a href={ templ.URL(project.LinkGithub) } target="_blank"
|
||||
class={ helpers.TemplIf(project.IsWIP, "pointer-events-none", "pointer-events-auto") + " inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[0.6875rem] font-medium bg-zinc-800 text-fg-200 border border-zinc-700 hover:border-zinc-500 hover:text-fg-100 transition-all duration-150"}>
|
||||
@icons.Github("size-4")
|
||||
GitHub
|
||||
@icons.ArrowUpRight("size-3")
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
} else {
|
||||
<div class="pointer-events-none inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[0.6875rem] font-medium bg-zinc-800 text-fg-200 border border-zinc-700 hover:border-zinc-500 hover:text-fg-100 transition-all duration-150">
|
||||
@icons.Ban("size-4")
|
||||
Closed Source
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="arrow relative z-10 text-fg-200 mt-0.5 shrink-0 opacity-0 group-hover:opacity-100 transition-all duration-150 ease-in-out"
|
||||
>
|
||||
class="arrow relative z-10 text-fg-200 mt-0.5 shrink-0 opacity-0 group-hover:opacity-100 transition-all duration-150 ease-in-out">
|
||||
@icons.ArrowUpRight("size-4")
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user