De-template the frontend and apply the design system

- FastAPI branding replaced throughout: title, favicon, logo (now the org/
  brand SVGs), footer links, route titles; workspace package renamed
- shadcn primitives restyled to the component radius/shadow map: pill
  controls, rounded-lg surfaces, shadow-e1/e2/e3
- floating frosted AppSidebar over a bg-card content region
- theme storage key aligned with the pre-paint script, MotionConfig added
- scripts/capture-screenshots.mjs backs the root 'make verify'

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Melvin Strobl
2026-08-09 15:33:56 +02:00
co-authored by Claude Opus 5
parent bd2155603e
commit 5988822053
41 changed files with 727 additions and 408 deletions
+3
View File
@@ -15,3 +15,6 @@ __pycache__/
# Rendered pipeline graphs (matplotlib output from backend/app/flow). # Rendered pipeline graphs (matplotlib output from backend/app/flow).
pipeline.png pipeline.png
# Output of `make verify` (root Makefile).
/frontend/screenshots/
+9
View File
@@ -33,6 +33,7 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"form-data": "4.0.5", "form-data": "4.0.5",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",
"motion": "^13.0.0",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
@@ -548,6 +549,8 @@
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
"framer-motion": ["framer-motion@13.0.0", "", { "dependencies": { "motion-dom": "^13.0.0", "motion-utils": "^13.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-nQGZXlsiigN48nzvE7AL1GLekql+etcphp8v+PQ0X04oxO20yVmV9rU9XQ25c136RdeIYoaWlKT9oAwvJza3mg=="],
"frontend": ["frontend@workspace:frontend"], "frontend": ["frontend@workspace:frontend"],
"fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
@@ -658,6 +661,12 @@
"mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
"motion": ["motion@13.0.0", "", { "dependencies": { "framer-motion": "^13.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-RYZjEpgHUCKjBNqjmUQzW93VJWLvEZGTPdKRFSqOdOl07IIMvz+WrsZmD1XXAkoFLruW/8bO1L7UF+ViQTxtLg=="],
"motion-dom": ["motion-dom@13.0.0", "", { "dependencies": { "motion-utils": "^13.0.0" } }, "sha512-Xk+SJas70uMAUIApg+m3lZDShxI3LBFHq7mFGbBKoRXc2PVPDyAKmzN64Bbzt4CZdP/CItTiJxWtn4TA0v53Ng=="],
"motion-utils": ["motion-utils@13.0.0", "", {}, "sha512-7DnN7TmbLcYXcG4RVadXIihWlyuM9afoUww8Y5Agg431kGKiuL2/OMyP4mJ5wLz+pvN3t5ySClLOaVXJ+wekRQ=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+14 -3
View File
@@ -2,10 +2,21 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Full Stack FastAPI Project</title> <meta name="theme-color" content="#59849b" />
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.png" /> <title>Fluksio</title>
<!-- Set the theme class before first paint so the app never flashes light. -->
<script>
(() => {
try {
const stored = localStorage.getItem('fluksio-ui-theme')
const theme = stored && stored !== 'system' ? stored
: (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
document.documentElement.classList.add(theme)
} catch (_e) { }
})()
</script>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+1
View File
@@ -37,6 +37,7 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"form-data": "4.0.5", "form-data": "4.0.5",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",
"motion": "^13.0.0",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="500"
height="500"
viewBox="0 0 132.29167 132.29166"
version="1.1"
id="svg8"
sodipodi:docname="icon-white-nomargin-transparent.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
inkscape:export-filename="icon-teal-nomargin-transparent-500.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1720"
inkscape:window-height="1371"
id="namedview10"
showgrid="false"
inkscape:zoom="0.73657628"
inkscape:cx="448.01877"
inkscape:cy="-91.640203"
inkscape:window-x="1720"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="g1"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm" />
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g2149">
<g
id="g2141">
<g
id="g1">
<path
id="path875-5-9-7-3-2-3-9-9-8-0-0-5-87-7"
style="fill:#ffffff;fill-opacity:0.980392;stroke:none;stroke-width:0.28098;stop-color:#000000"
d="M 66.145833 0 A 66.145836 65.931923 0 0 0 0 65.931893 A 66.145836 65.931923 0 0 0 66.145833 131.86379 A 66.145836 65.931923 0 0 0 132.29167 65.931893 A 66.145836 65.931923 0 0 0 66.145833 0 z M 61.581771 29.853992 L 103.20042 29.853992 L 61.410205 59.222742 L 89.976937 59.222742 L 29.091248 102.00979 L 42.315763 72.641044 L 48.358289 59.222742 L 61.581771 29.853992 z " />
</g>
</g>
</g>
<rect
y="-49.422424"
x="-51.908718"
height="162.82199"
width="451.52316"
id="rect824"
style="opacity:0.98;fill:none;fill-opacity:1;stroke-width:0.311037" />
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="500"
height="500"
viewBox="0 0 132.29167 132.29166"
version="1.1"
id="svg8"
sodipodi:docname="icon-teal-nomargin-transparent.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
inkscape:export-filename="icon-teal-nomargin-192.png"
inkscape:export-xdpi="36.863998"
inkscape:export-ydpi="36.863998"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1720"
inkscape:window-height="1371"
id="namedview10"
showgrid="false"
inkscape:zoom="0.73657628"
inkscape:cx="448.01877"
inkscape:cy="-91.640203"
inkscape:window-x="1720"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="g1"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm" />
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g2149">
<g
id="g2141">
<g
id="g1">
<path
id="path875-5-9-7-3-2-3-9-9-8-0-0-5-87-7"
style="fill:#009688;fill-opacity:0.980392;stroke:none;stroke-width:0.28098;stop-color:#000000"
d="M 66.145833 0 A 66.145836 65.931923 0 0 0 0 65.931893 A 66.145836 65.931923 0 0 0 66.145833 131.86379 A 66.145836 65.931923 0 0 0 132.29167 65.931893 A 66.145836 65.931923 0 0 0 66.145833 0 z M 61.581771 29.853992 L 103.20042 29.853992 L 61.410205 59.222742 L 89.976937 59.222742 L 29.091248 102.00979 L 42.315763 72.641044 L 48.358289 59.222742 L 61.581771 29.853992 z " />
</g>
</g>
</g>
<rect
y="-49.422424"
x="-51.908718"
height="162.82199"
width="451.52316"
id="rect824"
style="opacity:0.98;fill:none;fill-opacity:1;stroke-width:0.311037" />
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="341.26324mm"
height="63.977489mm"
viewBox="0 0 341.26324 63.977485"
version="1.1"
id="svg8"
sodipodi:docname="logo-white-nomargin-vector.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
inkscape:export-filename="logo-white-margin-vector.png"
inkscape:export-xdpi="57.604134"
inkscape:export-ydpi="57.604134"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1720"
inkscape:window-height="1371"
id="namedview10"
showgrid="false"
inkscape:zoom="0.73657628"
inkscape:cx="644.8755"
inkscape:cy="95.713101"
inkscape:window-x="1720"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="g2141"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm" />
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g2149"
transform="translate(2.5752783e-4,1.1668595e-4)">
<g
id="g2141">
<g
id="g1">
<path
id="path875-5-9-7-3-2-3-9-9-8-0-0-5-87-7"
style="fill:#ffffff;fill-opacity:0.980392;stroke:none;stroke-width:0.136325;stop-color:#000000"
d="M 32.092357,-1.166849e-4 A 32.092354,31.988569 0 0 0 -2.5752734e-4,31.988628 32.092354,31.988569 0 0 0 32.092357,63.977374 32.092354,31.988569 0 0 0 64.184455,31.988628 32.092354,31.988569 0 0 0 32.092357,-1.166849e-4 Z M 29.878022,14.484271 H 50.070588 L 29.794823,28.73353 H 43.654442 L 14.114126,49.49247 20.530789,35.243727 23.462393,28.73353 Z" />
<path
style="font-size:79.7151px;line-height:1.25;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;letter-spacing:0px;word-spacing:0px;fill:#ffffff;stroke-width:1.99288"
d="M 89.762163,59.410606 V 4.1680399 H 121.88735 V 9.5089518 H 95.979941 V 28.082571 h 22.957949 v 5.340912 H 95.979941 v 25.987123 z m 51.017587,0.876867 q -4.46405,0 -7.97152,-1.275442 -3.50746,-1.275442 -5.50034,-4.145185 -1.99288,-2.869744 -1.99288,-7.572935 0,-4.543761 2.23203,-7.33379 2.23202,-2.869743 6.13806,-4.145185 3.98576,-1.275442 8.92809,-1.275442 2.23203,0 4.70319,0.398576 2.47117,0.398575 3.10889,0.717436 v -2.391453 q 0,-2.710314 -0.71743,-5.181482 -0.63772,-2.550883 -2.71032,-4.145186 -2.07259,-1.594302 -6.29749,-1.594302 -4.38433,0 -6.69607,0.637721 -2.31174,0.637721 -3.42775,1.036297 l -0.79715,-5.101767 q 1.43487,-0.637721 4.38433,-1.195727 2.94946,-0.558005 6.93522,-0.558005 5.65977,0 8.92809,1.992877 3.34803,1.992878 4.7829,5.500342 1.51459,3.42775 1.51459,7.891796 v 25.907408 q -1.67402,0.398576 -5.97863,1.116012 -4.30462,0.717436 -9.56581,0.717436 z m 0.87686,-5.101767 q 2.79003,0 5.02205,-0.15943 2.23203,-0.239146 3.74661,-0.558006 V 40.597842 q -0.79715,-0.398575 -2.71031,-0.797151 -1.91316,-0.398576 -4.94234,-0.398576 -2.55088,0 -5.18148,0.558006 -2.6306,0.558006 -4.46404,2.232023 -1.75374,1.674017 -1.75374,5.022052 0,4.464045 2.79003,6.217778 2.79003,1.753732 7.49322,1.753732 z m 36.4298,5.181482 q -5.34092,0 -8.37009,-0.956582 -2.94946,-0.876866 -3.98575,-1.355156 l 1.43487,-5.261197 q 0.87686,0.31886 3.58718,1.355157 2.71031,1.036296 7.33379,1.036296 4.38433,0 7.09464,-1.355157 2.71031,-1.355157 2.71031,-4.703191 0,-2.152308 -0.95658,-3.427749 -0.95658,-1.355157 -3.10889,-2.471169 -2.07259,-1.116011 -5.73948,-2.550883 -3.10889,-1.275442 -5.73949,-2.710313 -2.6306,-1.434872 -4.30462,-3.666895 -1.5943,-2.232023 -1.5943,-5.739488 0,-3.427749 1.75373,-5.978632 1.75374,-2.550884 4.94234,-3.985755 3.26832,-1.434872 7.73237,-1.434872 4.14518,0 7.01492,0.717436 2.86975,0.717436 4.06547,1.275441 l -1.35515,5.181482 q -1.0363,-0.558006 -3.34804,-1.275442 -2.31173,-0.797151 -6.53663,-0.797151 -3.34804,0 -5.81921,1.434872 -2.47117,1.355157 -2.47117,4.384331 0,2.152308 1.0363,3.507464 1.0363,1.275442 3.10889,2.311738 2.07259,1.036297 5.10177,2.232023 3.42775,1.355157 6.13806,2.869744 2.79003,1.434872 4.46405,3.74661 1.67401,2.311738 1.67401,6.138063 0,3.74661 -1.91316,6.377208 -1.91316,2.550883 -5.50034,3.826325 -3.50747,1.275442 -8.4498,1.275442 z m 39.21967,-0.07972 q -5.2612,0 -8.29037,-1.833448 -3.02917,-1.833447 -4.30461,-5.500342 -1.19573,-3.666895 -1.19573,-9.167237 V 6.1609175 L 209.494,5.1246211 V 18.118183 h 16.18217 v 5.022051 H 209.494 v 21.124503 q 0,4.38433 0.95658,6.696068 1.0363,2.311738 2.86975,3.188605 1.91316,0.797151 4.46404,0.797151 3.02918,0 5.02205,-0.717436 2.0726,-0.717436 3.26832,-1.275442 l 1.27544,4.862621 q -1.19572,0.717436 -3.98575,1.594302 -2.71031,0.876867 -6.05835,0.876867 z m 11.95723,-0.876867 q 4.30462,-11.55869 7.8918,-21.044787 3.58718,-9.486097 7.01493,-17.776468 3.50746,-8.370086 7.4135,-16.4213111 h 5.58006 q 2.86974,6.0583481 5.50034,12.2761261 2.71032,6.138063 5.34091,12.754416 2.6306,6.616354 5.42063,14.109574 2.86975,7.413504 6.05835,16.10245 h -6.77578 q -1.43488,-3.985755 -2.79003,-7.572934 -1.35516,-3.666895 -2.55089,-7.17436 h -26.30598 q -1.27544,3.507465 -2.6306,7.17436 -1.35516,3.587179 -2.71031,7.572934 z M 242.8946,39.402115 h 22.63909 q -1.51459,-3.985755 -2.94946,-7.732365 -1.43487,-3.746609 -2.86975,-7.254074 -1.35515,-3.507464 -2.79002,-6.775784 -1.35516,-3.348034 -2.79003,-6.536638 -1.35516,3.188604 -2.79003,6.536638 -1.35516,3.26832 -2.79003,6.775784 -1.35516,3.507465 -2.79003,7.254074 -1.43487,3.74661 -2.86974,7.732365 z m 44.481,20.008491 V 5.2043362 q 3.02917,-0.797151 6.93521,-1.1160114 3.98576,-0.3985755 7.33379,-0.3985755 11.71812,0 17.53732,4.5437609 5.89892,4.4640458 5.89892,12.7544168 0,6.297493 -2.94946,10.123818 -2.86974,3.826325 -8.37008,5.580057 -5.42063,1.674017 -13.07328,1.674017 h -7.09465 v 21.044787 z m 6.21777,-26.385699 h 6.53664 q 5.65978,0 9.80496,-0.956581 4.14519,-1.036296 6.37721,-3.666895 2.31174,-2.630598 2.31174,-7.493219 0,-4.703192 -2.39146,-7.254075 -2.39145,-2.550883 -6.21777,-3.587179 -3.82633,-1.0362968 -8.13094,-1.0362968 -2.79003,0 -4.86263,0.2391453 -1.99287,0.1594302 -3.42775,0.3188604 z M 335.0452,59.410606 V 4.1680399 h 6.21778 V 59.410606 Z"
id="text979-3"
aria-label="FastAPI" />
</g>
</g>
</g>
<rect
y="-49.422306"
x="-51.908459"
height="162.82199"
width="451.52316"
id="rect824"
style="opacity:0.98;fill:none;fill-opacity:1;stroke-width:0.311037" />
</svg>

Before

Width:  |  Height:  |  Size: 7.0 KiB

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="341.26297mm"
height="63.977139mm"
viewBox="0 0 341.26297 63.977134"
version="1.1"
id="svg8"
sodipodi:docname="logo-teal-nomargin-vector.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1720"
inkscape:window-height="1371"
id="namedview10"
showgrid="false"
inkscape:zoom="0.73657628"
inkscape:cx="448.01877"
inkscape:cy="-91.640203"
inkscape:window-x="1720"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="g1"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm" />
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g2149">
<g
id="g2141">
<g
id="g1">
<g
id="g2106"
transform="matrix(0.96564264,0,0,0.96251987,-899.3295,194.86874)">
<circle
style="fill:#009688;fill-opacity:0.980392;stroke:none;stroke-width:0.141404;stop-color:#000000"
id="path875-5-9-7-3-2-3-9-9-8-0-0-5-87-7"
cx="964.56165"
cy="-169.22266"
r="33.234192"
inkscape:export-xdpi="1543.8315"
inkscape:export-ydpi="1543.8315" />
<path
id="rect1249-6-3-4-4-3-6-6-1-2"
style="fill:#ffffff;fill-opacity:0.980392;stroke:none;stroke-width:0.146895;stop-color:#000000"
d="m 962.2685,-187.40837 -6.64403,14.80375 -3.03599,6.76393 -6.64456,14.80375 30.59142,-21.56768 h -14.35312 l 20.99715,-14.80375 z" />
</g>
<path
style="font-size:79.7151px;line-height:1.25;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;letter-spacing:0px;word-spacing:0px;fill:#009688;stroke-width:1.99288"
d="M 89.762163,59.410606 V 4.1680399 H 121.88735 V 9.5089518 H 95.979941 V 28.082571 h 22.957949 v 5.340912 H 95.979941 v 25.987123 z m 51.017587,0.876867 q -4.46405,0 -7.97152,-1.275442 -3.50746,-1.275442 -5.50034,-4.145185 -1.99288,-2.869744 -1.99288,-7.572935 0,-4.543761 2.23203,-7.33379 2.23202,-2.869743 6.13806,-4.145185 3.98576,-1.275442 8.92809,-1.275442 2.23203,0 4.70319,0.398576 2.47117,0.398575 3.10889,0.717436 v -2.391453 q 0,-2.710314 -0.71743,-5.181482 -0.63772,-2.550883 -2.71032,-4.145186 -2.07259,-1.594302 -6.29749,-1.594302 -4.38433,0 -6.69607,0.637721 -2.31174,0.637721 -3.42775,1.036297 l -0.79715,-5.101767 q 1.43487,-0.637721 4.38433,-1.195727 2.94946,-0.558005 6.93522,-0.558005 5.65977,0 8.92809,1.992877 3.34803,1.992878 4.7829,5.500342 1.51459,3.42775 1.51459,7.891796 v 25.907408 q -1.67402,0.398576 -5.97863,1.116012 -4.30462,0.717436 -9.56581,0.717436 z m 0.87686,-5.101767 q 2.79003,0 5.02205,-0.15943 2.23203,-0.239146 3.74661,-0.558006 V 40.597842 q -0.79715,-0.398575 -2.71031,-0.797151 -1.91316,-0.398576 -4.94234,-0.398576 -2.55088,0 -5.18148,0.558006 -2.6306,0.558006 -4.46404,2.232023 -1.75374,1.674017 -1.75374,5.022052 0,4.464045 2.79003,6.217778 2.79003,1.753732 7.49322,1.753732 z m 36.4298,5.181482 q -5.34092,0 -8.37009,-0.956582 -2.94946,-0.876866 -3.98575,-1.355156 l 1.43487,-5.261197 q 0.87686,0.31886 3.58718,1.355157 2.71031,1.036296 7.33379,1.036296 4.38433,0 7.09464,-1.355157 2.71031,-1.355157 2.71031,-4.703191 0,-2.152308 -0.95658,-3.427749 -0.95658,-1.355157 -3.10889,-2.471169 -2.07259,-1.116011 -5.73948,-2.550883 -3.10889,-1.275442 -5.73949,-2.710313 -2.6306,-1.434872 -4.30462,-3.666895 -1.5943,-2.232023 -1.5943,-5.739488 0,-3.427749 1.75373,-5.978632 1.75374,-2.550884 4.94234,-3.985755 3.26832,-1.434872 7.73237,-1.434872 4.14518,0 7.01492,0.717436 2.86975,0.717436 4.06547,1.275441 l -1.35515,5.181482 q -1.0363,-0.558006 -3.34804,-1.275442 -2.31173,-0.797151 -6.53663,-0.797151 -3.34804,0 -5.81921,1.434872 -2.47117,1.355157 -2.47117,4.384331 0,2.152308 1.0363,3.507464 1.0363,1.275442 3.10889,2.311738 2.07259,1.036297 5.10177,2.232023 3.42775,1.355157 6.13806,2.869744 2.79003,1.434872 4.46405,3.74661 1.67401,2.311738 1.67401,6.138063 0,3.74661 -1.91316,6.377208 -1.91316,2.550883 -5.50034,3.826325 -3.50747,1.275442 -8.4498,1.275442 z m 39.21967,-0.07972 q -5.2612,0 -8.29037,-1.833448 -3.02917,-1.833447 -4.30461,-5.500342 -1.19573,-3.666895 -1.19573,-9.167237 V 6.1609175 L 209.494,5.1246211 V 18.118183 h 16.18217 v 5.022051 H 209.494 v 21.124503 q 0,4.38433 0.95658,6.696068 1.0363,2.311738 2.86975,3.188605 1.91316,0.797151 4.46404,0.797151 3.02918,0 5.02205,-0.717436 2.0726,-0.717436 3.26832,-1.275442 l 1.27544,4.862621 q -1.19572,0.717436 -3.98575,1.594302 -2.71031,0.876867 -6.05835,0.876867 z m 11.95723,-0.876867 q 4.30462,-11.55869 7.8918,-21.044787 3.58718,-9.486097 7.01493,-17.776468 3.50746,-8.370086 7.4135,-16.4213111 h 5.58006 q 2.86974,6.0583481 5.50034,12.2761261 2.71032,6.138063 5.34091,12.754416 2.6306,6.616354 5.42063,14.109574 2.86975,7.413504 6.05835,16.10245 h -6.77578 q -1.43488,-3.985755 -2.79003,-7.572934 -1.35516,-3.666895 -2.55089,-7.17436 h -26.30598 q -1.27544,3.507465 -2.6306,7.17436 -1.35516,3.587179 -2.71031,7.572934 z M 242.8946,39.402115 h 22.63909 q -1.51459,-3.985755 -2.94946,-7.732365 -1.43487,-3.746609 -2.86975,-7.254074 -1.35515,-3.507464 -2.79002,-6.775784 -1.35516,-3.348034 -2.79003,-6.536638 -1.35516,3.188604 -2.79003,6.536638 -1.35516,3.26832 -2.79003,6.775784 -1.35516,3.507465 -2.79003,7.254074 -1.43487,3.74661 -2.86974,7.732365 z m 44.481,20.008491 V 5.2043362 q 3.02917,-0.797151 6.93521,-1.1160114 3.98576,-0.3985755 7.33379,-0.3985755 11.71812,0 17.53732,4.5437609 5.89892,4.4640458 5.89892,12.7544168 0,6.297493 -2.94946,10.123818 -2.86974,3.826325 -8.37008,5.580057 -5.42063,1.674017 -13.07328,1.674017 h -7.09465 v 21.044787 z m 6.21777,-26.385699 h 6.53664 q 5.65978,0 9.80496,-0.956581 4.14519,-1.036296 6.37721,-3.666895 2.31174,-2.630598 2.31174,-7.493219 0,-4.703192 -2.39146,-7.254075 -2.39145,-2.550883 -6.21777,-3.587179 -3.82633,-1.0362968 -8.13094,-1.0362968 -2.79003,0 -4.86263,0.2391453 -1.99287,0.1594302 -3.42775,0.3188604 z M 335.0452,59.410606 V 4.1680399 h 6.21778 V 59.410606 Z"
id="text979-3"
aria-label="FastAPI" />
</g>
</g>
</g>
<rect
y="-49.422424"
x="-51.908718"
height="162.82199"
width="451.52316"
id="rect824"
style="opacity:0.98;fill:none;fill-opacity:1;stroke-width:0.311037" />
</svg>

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg
width="800px"
height="800px"
viewBox="0 0 24 24"
fill="none"
version="1.1"
id="svg8"
sodipodi:docname="fluksio-dark.svg"
inkscape:version="1.4.3 (unknown)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect8"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview8"
pagecolor="#777777"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showguides="true"
showgrid="true"
inkscape:zoom="1.1425"
inkscape:cx="400"
inkscape:cy="400"
inkscape:window-width="2207"
inkscape:window-height="1163"
inkscape:window-x="20"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg8"
showborder="true">
<inkscape:grid
id="grid8"
units="px"
originx="0"
originy="0"
spacingx="0.03"
spacingy="0.03"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
enabled="true"
visible="true" />
</sodipodi:namedview>
<path
style="fill:#f5f9e9;fill-opacity:1;stroke:none;stroke-width:1.03333;stroke-dasharray:none;stroke-opacity:1"
id="rect8"
width="18.599998"
height="18.6"
x="2.7000008"
y="2.7"
sodipodi:type="rect"
inkscape:path-effect="#path-effect9"
d="M 9.0000005,2.7 H 15 c 3.490199,0 6.299999,2.8097999 6.299999,6.2999998 V 15.000001 C 21.299999,18.490201 18.490199,21.3 15,21.3 H 9.0000005 C 5.5098006,21.3 2.7000008,18.490201 2.7000008,15.000001 V 8.9999998 C 2.7000008,5.5097999 5.5098006,2.7 9.0000005,2.7 Z"
rx="6.2999997"
ry="6.2999997"
transform="matrix(1.2903227,0,0,1.2903226,-3.4838723,-3.483871)" />
<path
d="m 13.818182,5.9393942 4.242425,4.2424248"
stroke="#000000"
stroke-linecap="round"
id="path1"
style="stroke:#333232;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="m 5.9393943,13.818182 4.2424247,4.242425"
stroke="#000000"
stroke-linecap="round"
id="path2"
style="stroke:#333232;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="M 10.181819,5.9393942 5.9393943,10.181819"
stroke="#000000"
stroke-linecap="round"
id="path3"
style="stroke:#333232;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="m 18.060607,13.818182 -4.242425,4.242425"
stroke="#000000"
stroke-linecap="round"
id="path4"
style="stroke:#333232;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="4.1212125"
r="2.4242425"
stroke="#000000"
id="circle4"
style="stroke:#de8f6e;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="19.87879"
r="2.4242425"
stroke="#000000"
id="circle5"
style="stroke:#333232;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="4.1212125"
cy="12"
r="2.4242425"
stroke="#000000"
id="circle6"
style="stroke:#de8f6e;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="19.87879"
cy="12"
r="2.4242425"
stroke="#000000"
id="circle7"
style="stroke:#333232;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="4.1212125"
cy="12"
r="0.60606062"
stroke="#000000"
id="circle8"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="19.87879"
cy="12"
r="0.60606062"
stroke="#000000"
id="circle8-3"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12.000007"
cy="4.1212182"
r="0.60606062"
stroke="#000000"
id="circle8-9"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="19.87879"
r="0.60606062"
stroke="#000000"
id="circle8-4"
style="fill:none;fill-opacity:1;stroke:#de8f6e;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg
width="800px"
height="800px"
viewBox="0 0 24 24"
fill="none"
version="1.1"
id="svg8"
sodipodi:docname="fluksio-light.svg"
inkscape:version="1.4.3 (unknown)"
inkscape:export-filename="fluksio-light.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect8"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview8"
pagecolor="#777777"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showguides="true"
showgrid="true"
inkscape:zoom="1.1425"
inkscape:cx="400"
inkscape:cy="400"
inkscape:window-width="2207"
inkscape:window-height="1163"
inkscape:window-x="20"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg8"
showborder="true">
<inkscape:grid
id="grid8"
units="px"
originx="0"
originy="0"
spacingx="0.03"
spacingy="0.03"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
enabled="true"
visible="true" />
</sodipodi:namedview>
<path
style="fill:#333232;fill-opacity:1;stroke:none;stroke-width:1.03333;stroke-dasharray:none;stroke-opacity:1"
id="rect8"
width="18.599998"
height="18.6"
x="2.7000008"
y="2.7"
sodipodi:type="rect"
inkscape:path-effect="#path-effect9"
d="M 9.0000005,2.7 H 15 c 3.490199,0 6.299999,2.8097999 6.299999,6.2999998 V 15.000001 C 21.299999,18.490201 18.490199,21.3 15,21.3 H 9.0000005 C 5.5098006,21.3 2.7000008,18.490201 2.7000008,15.000001 V 8.9999998 C 2.7000008,5.5097999 5.5098006,2.7 9.0000005,2.7 Z"
rx="6.2999997"
ry="6.2999997"
transform="matrix(1.2903227,0,0,1.2903225,-3.4838724,-3.4838706)" />
<path
d="m 13.818183,5.9393946 4.242424,4.2424244"
stroke="#000000"
stroke-linecap="round"
id="path1"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="m 5.9393944,13.818183 4.2424246,4.242424"
stroke="#000000"
stroke-linecap="round"
id="path2"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="M 10.181819,5.9393946 5.9393944,10.181819"
stroke="#000000"
stroke-linecap="round"
id="path3"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="m 18.060607,13.818183 -4.242424,4.242424"
stroke="#000000"
stroke-linecap="round"
id="path4"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="4.1212125"
r="2.4242425"
stroke="#000000"
id="circle4"
style="stroke:#de8f6e;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="19.87879"
r="2.4242425"
stroke="#000000"
id="circle5"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="4.1212125"
cy="12.000001"
r="2.4242425"
stroke="#000000"
id="circle6"
style="stroke:#de8f6e;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="19.87879"
cy="12.000001"
r="2.4242425"
stroke="#000000"
id="circle7"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="4.1212125"
cy="12.000001"
r="0.60606062"
stroke="#000000"
id="circle8"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="19.87879"
cy="12.000001"
r="0.60606062"
stroke="#000000"
id="circle8-3"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12.000007"
cy="4.1212187"
r="0.60606062"
stroke="#000000"
id="circle8-9"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="19.87879"
r="0.60606062"
stroke="#000000"
id="circle8-4"
style="fill:none;fill-opacity:1;stroke:#de8f6e;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

+186
View File
@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg
width="800px"
height="800px"
viewBox="0 0 24 24"
fill="none"
version="1.1"
id="svg8"
sodipodi:docname="fluksio-light.svg"
inkscape:version="1.4.3 (unknown)"
inkscape:export-filename="fluksio-light.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect8"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview8"
pagecolor="#777777"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showguides="true"
showgrid="true"
inkscape:zoom="1.1425"
inkscape:cx="400"
inkscape:cy="400"
inkscape:window-width="2207"
inkscape:window-height="1163"
inkscape:window-x="20"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg8"
showborder="true">
<inkscape:grid
id="grid8"
units="px"
originx="0"
originy="0"
spacingx="0.03"
spacingy="0.03"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
enabled="true"
visible="true" />
</sodipodi:namedview>
<path
style="fill:#333232;fill-opacity:1;stroke:none;stroke-width:1.03333;stroke-dasharray:none;stroke-opacity:1"
id="rect8"
width="18.599998"
height="18.6"
x="2.7000008"
y="2.7"
sodipodi:type="rect"
inkscape:path-effect="#path-effect9"
d="M 9.0000005,2.7 H 15 c 3.490199,0 6.299999,2.8097999 6.299999,6.2999998 V 15.000001 C 21.299999,18.490201 18.490199,21.3 15,21.3 H 9.0000005 C 5.5098006,21.3 2.7000008,18.490201 2.7000008,15.000001 V 8.9999998 C 2.7000008,5.5097999 5.5098006,2.7 9.0000005,2.7 Z"
rx="6.2999997"
ry="6.2999997"
transform="matrix(1.2903227,0,0,1.2903225,-3.4838724,-3.4838706)" />
<path
d="m 13.818183,5.9393946 4.242424,4.2424244"
stroke="#000000"
stroke-linecap="round"
id="path1"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="m 5.9393944,13.818183 4.2424246,4.242424"
stroke="#000000"
stroke-linecap="round"
id="path2"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="M 10.181819,5.9393946 5.9393944,10.181819"
stroke="#000000"
stroke-linecap="round"
id="path3"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<path
d="m 18.060607,13.818183 -4.242424,4.242424"
stroke="#000000"
stroke-linecap="round"
id="path4"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="4.1212125"
r="2.4242425"
stroke="#000000"
id="circle4"
style="stroke:#de8f6e;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="19.87879"
r="2.4242425"
stroke="#000000"
id="circle5"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="4.1212125"
cy="12.000001"
r="2.4242425"
stroke="#000000"
id="circle6"
style="stroke:#de8f6e;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="19.87879"
cy="12.000001"
r="2.4242425"
stroke="#000000"
id="circle7"
style="stroke:#f5f9e9;stroke-width:1.45454;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="4.1212125"
cy="12.000001"
r="0.60606062"
stroke="#000000"
id="circle8"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="19.87879"
cy="12.000001"
r="0.60606062"
stroke="#000000"
id="circle8-3"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12.000007"
cy="4.1212187"
r="0.60606062"
stroke="#000000"
id="circle8-9"
style="fill:none;fill-opacity:1;stroke:#59849b;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
<circle
cx="12"
cy="19.87879"
r="0.60606062"
stroke="#000000"
id="circle8-4"
style="fill:none;fill-opacity:1;stroke:#de8f6e;stroke-width:1.21211;stroke-dasharray:none;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

+65
View File
@@ -0,0 +1,65 @@
/**
* Visual verification for the integrated local stack (root `make verify`).
*
* Logs into the dashboard with the bootstrap superuser and captures the app
* shell plus the website hero in both themes. This is the standard "did the
* change actually work" gate — look at the PNGs, do not just trust the build.
*
* Env (all set by the root Makefile):
* APP_URL, WEBSITE_URL, FIRST_SUPERUSER, FIRST_SUPERUSER_PASSWORD
* SCREENSHOT_DIR (default: ./screenshots)
*/
import { mkdir } from "node:fs/promises"
import { chromium } from "@playwright/test"
const APP_URL = process.env.APP_URL || "http://app.localhost"
const WEBSITE_URL = process.env.WEBSITE_URL || "http://localhost"
const EMAIL = process.env.FIRST_SUPERUSER
const PASSWORD = process.env.FIRST_SUPERUSER_PASSWORD
const OUT = process.env.SCREENSHOT_DIR || "screenshots"
if (!EMAIL || !PASSWORD) {
console.error(
"FIRST_SUPERUSER / FIRST_SUPERUSER_PASSWORD are unset — run from the root `make verify`.",
)
process.exit(1)
}
/** Force the theme through the same storage key the pre-paint script reads. */
async function withTheme(browser, theme) {
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
colorScheme: theme,
})
await context.addInitScript((t) => {
localStorage.setItem("fluksio-ui-theme", t)
}, theme)
return context
}
const browser = await chromium.launch()
for (const theme of ["light", "dark"]) {
const dir = `${OUT}/${theme}`
await mkdir(dir, { recursive: true })
const context = await withTheme(browser, theme)
const page = await context.newPage()
await page.goto(`${WEBSITE_URL}/`, { waitUntil: "networkidle" })
await page.screenshot({ path: `${dir}/website-hero.png` })
await page.goto(`${APP_URL}/login`, { waitUntil: "networkidle" })
await page.screenshot({ path: `${dir}/app-login.png` })
await page.getByPlaceholder(/email/i).fill(EMAIL)
await page.getByPlaceholder(/password/i).fill(PASSWORD)
await page.getByRole("button", { name: /log in/i }).click()
await page.waitForURL(`${APP_URL}/`, { timeout: 15000 })
await page.waitForLoadState("networkidle")
await page.screenshot({ path: `${dir}/app-dashboard.png` })
await context.close()
console.log(` wrote ${dir}/{website-hero,app-login,app-dashboard}.png`)
}
await browser.close()
+5 -14
View File
@@ -1,17 +1,10 @@
import { FaGithub, FaLinkedinIn } from "react-icons/fa" import { SiCodeberg } from "react-icons/si"
import { FaXTwitter } from "react-icons/fa6"
const socialLinks = [ const socialLinks = [
{ {
icon: FaGithub, icon: SiCodeberg,
href: "https://github.com/fastapi/fastapi", href: "https://codeberg.org/fluksio",
label: "GitHub", label: "Codeberg",
},
{ icon: FaXTwitter, href: "https://x.com/fastapi", label: "X" },
{
icon: FaLinkedinIn,
href: "https://linkedin.com/company/fastapi",
label: "LinkedIn",
}, },
] ]
@@ -21,9 +14,7 @@ export function Footer() {
return ( return (
<footer className="border-t py-4 px-6"> <footer className="border-t py-4 px-6">
<div className="flex flex-col items-center justify-between gap-4 sm:flex-row"> <div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
<p className="text-muted-foreground text-sm"> <p className="text-muted-foreground text-sm">Fluksio - {currentYear}</p>
Full Stack FastAPI Template - {currentYear}
</p>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{socialLinks.map(({ icon: Icon, href, label }) => ( {socialLinks.map(({ icon: Icon, href, label }) => (
<a <a
+16 -16
View File
@@ -2,10 +2,8 @@ import { Link } from "@tanstack/react-router"
import { useTheme } from "@/components/theme-provider" import { useTheme } from "@/components/theme-provider"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import icon from "/assets/images/fastapi-icon.svg" import logoDark from "/assets/images/fluksio-dark.svg"
import iconLight from "/assets/images/fastapi-icon-light.svg" import logo from "/assets/images/fluksio-light.svg"
import logo from "/assets/images/fastapi-logo.svg"
import logoLight from "/assets/images/fastapi-logo-light.svg"
interface LogoProps { interface LogoProps {
variant?: "full" | "icon" | "responsive" variant?: "full" | "icon" | "responsive"
@@ -19,36 +17,38 @@ export function Logo({
asLink = true, asLink = true,
}: LogoProps) { }: LogoProps) {
const { resolvedTheme } = useTheme() const { resolvedTheme } = useTheme()
const isDark = resolvedTheme === "dark" const src = resolvedTheme === "dark" ? logoDark : logo
const fullLogo = isDark ? logoLight : logo
const iconLogo = isDark ? iconLight : icon
// The mark and the wordmark live in the same SVG, so the collapsed sidebar
// shows the same asset cropped to a square rather than a separate icon file.
const content = const content =
variant === "responsive" ? ( variant === "responsive" ? (
<> <>
<img <img
src={fullLogo} src={src}
alt="FastAPI" alt="Fluksio"
className={cn( className={cn(
"h-6 w-auto group-data-[collapsible=icon]:hidden", "h-6 w-auto group-data-[collapsible=icon]:hidden",
className, className,
)} )}
/> />
<img <img
src={iconLogo} src={src}
alt="FastAPI" alt="Fluksio"
className={cn( className={cn(
"size-5 hidden group-data-[collapsible=icon]:block", "size-5 object-contain hidden group-data-[collapsible=icon]:block",
className, className,
)} )}
/> />
</> </>
) : ( ) : (
<img <img
src={variant === "full" ? fullLogo : iconLogo} src={src}
alt="FastAPI" alt="Fluksio"
className={cn(variant === "full" ? "h-6 w-auto" : "size-5", className)} className={cn(
variant === "full" ? "h-6 w-auto" : "size-5 object-contain",
className,
)}
/> />
) )
@@ -25,7 +25,13 @@ export function AppSidebar() {
: baseItems : baseItems
return ( return (
<Sidebar collapsible="icon"> // Floating frosted chrome over the bg-card content region; see the root
// DESIGN-GUIDELINES.md → Shells and → Overlay surfaces & content chips.
<Sidebar
collapsible="icon"
variant="floating"
className="[&>[data-sidebar=sidebar]]:bg-card/80 [&>[data-sidebar=sidebar]]:backdrop-blur-md [&>[data-sidebar=sidebar]]:shadow-e2"
>
<SidebarHeader className="px-4 py-6 group-data-[collapsible=icon]:px-0 group-data-[collapsible=icon]:items-center"> <SidebarHeader className="px-4 py-6 group-data-[collapsible=icon]:px-0 group-data-[collapsible=icon]:items-center">
<Logo variant="responsive" /> <Logo variant="responsive" />
</SidebarHeader> </SidebarHeader>
+2 -2
View File
@@ -5,7 +5,7 @@ import { cn } from "@/lib/utils"
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator"
const buttonGroupVariants = cva( const buttonGroupVariants = cva(
"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2", "flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-full has-[>[data-slot=button-group]]:gap-2",
{ {
variants: { variants: {
orientation: { orientation: {
@@ -49,7 +49,7 @@ function ButtonGroupText({
return ( return (
<Comp <Comp
className={cn( className={cn(
"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4", "bg-muted flex items-center gap-2 rounded-full border px-4 text-sm font-medium shadow-none [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
+7 -4
View File
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{ {
variants: { variants: {
variant: { variant: {
@@ -13,17 +13,20 @@ const buttonVariants = cva(
destructive: destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline: outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", "border bg-background shadow-none hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary: secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80", "bg-secondary text-secondary-foreground hover:bg-secondary/80",
// The one terracotta call-to-action per view (DESIGN-GUIDELINES.md).
brand:
"bg-brand-secondary text-brand-secondary-foreground hover:bg-brand-secondary/90",
ghost: ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3", default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", sm: "h-8 rounded-full gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4", lg: "h-10 rounded-full px-6 has-[>svg]:px-4",
icon: "size-9", icon: "size-9",
"icon-sm": "size-8", "icon-sm": "size-8",
"icon-lg": "size-10", "icon-lg": "size-10",
+1 -1
View File
@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card" data-slot="card"
className={cn( className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", "bg-card text-card-foreground flex flex-col gap-6 rounded-lg border py-6 shadow-e1",
className className
)} )}
{...props} {...props}
+1 -1
View File
@@ -12,7 +12,7 @@ function Checkbox({
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
data-slot="checkbox" data-slot="checkbox"
className={cn( className={cn(
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-none transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className className
)} )}
{...props} {...props}
+1 -1
View File
@@ -58,7 +58,7 @@ function DialogContent({
<DialogPrimitive.Content <DialogPrimitive.Content
data-slot="dialog-content" data-slot="dialog-content"
className={cn( className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg", "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-e3 duration-200 sm:max-w-lg",
className className
)} )}
{...props} {...props}
+2 -2
View File
@@ -42,7 +42,7 @@ function DropdownMenuContent({
data-slot="dropdown-menu-content" data-slot="dropdown-menu-content"
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-e2",
className className
)} )}
{...props} {...props}
@@ -230,7 +230,7 @@ function DropdownMenuSubContent({
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content" data-slot="dropdown-menu-sub-content"
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-e2",
className className
)} )}
{...props} {...props}
+1 -1
View File
@@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type} type={type}
data-slot="input" data-slot="input"
className={cn( className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-full border bg-transparent px-3 py-1 text-base shadow-none transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className className
@@ -4,26 +4,26 @@ import { Loader2 } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{ {
variants: { variants: {
variant: { variant: {
default: default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", "bg-primary text-primary-foreground shadow-none hover:bg-primary/90",
destructive: destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", "bg-destructive text-white shadow-none hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline: outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", "border bg-background shadow-none hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary: secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", "bg-secondary text-secondary-foreground shadow-none hover:bg-secondary/80",
ghost: ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3", default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", sm: "h-8 rounded-full gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4", lg: "h-10 rounded-full px-6 has-[>svg]:px-4",
icon: "size-9", icon: "size-9",
}, },
}, },
@@ -18,7 +18,7 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
data-slot="input" data-slot="input"
className={cn( className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 pr-10 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-full border bg-transparent px-3 py-1 pr-10 text-base shadow-none transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className className
+2 -2
View File
@@ -35,7 +35,7 @@ function SelectTrigger({
data-slot="select-trigger" data-slot="select-trigger"
data-size={size} data-size={size}
className={cn( className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-full border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-none transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@@ -60,7 +60,7 @@ function SelectContent({
<SelectPrimitive.Content <SelectPrimitive.Content
data-slot="select-content" data-slot="select-content"
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-e2",
position === "popper" && position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className className
+1 -1
View File
@@ -58,7 +58,7 @@ function SheetContent({
<SheetPrimitive.Content <SheetPrimitive.Content
data-slot="sheet-content" data-slot="sheet-content"
className={cn( className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500", "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-e2 transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
side === "right" && side === "right" &&
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm", "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
side === "left" && side === "left" &&
+3 -3
View File
@@ -254,7 +254,7 @@ function Sidebar({
<div <div
data-sidebar="sidebar" data-sidebar="sidebar"
data-slot="sidebar-inner" data-slot="sidebar-inner"
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm" className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-e1"
> >
{children} {children}
</div> </div>
@@ -321,7 +321,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
data-slot="sidebar-inset" data-slot="sidebar-inset"
className={cn( className={cn(
"bg-transparent relative flex w-full flex-1 flex-col", "bg-transparent relative flex w-full flex-1 flex-col",
"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2", "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-lg md:peer-data-[variant=inset]:shadow-e1 md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
className, className,
)} )}
{...props} {...props}
@@ -496,7 +496,7 @@ const sidebarMenuButtonVariants = cva(
size: { size: {
default: "h-8 text-sm rounded-lg", default: "h-8 text-sm rounded-lg",
sm: "h-7 text-xs", sm: "h-7 text-xs",
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0! rounded-xl data-[state=closed]:rounded-lg", lg: "h-12 text-sm group-data-[collapsible=icon]:p-0! rounded-lg data-[state=closed]:rounded-lg",
}, },
}, },
defaultVariants: { defaultVariants: {
+1 -1
View File
@@ -40,7 +40,7 @@ function TabsTrigger({
<TabsPrimitive.Trigger <TabsPrimitive.Trigger
data-slot="tabs-trigger" data-slot="tabs-trigger"
className={cn( className={cn(
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-full border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-e1 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
+4 -1
View File
@@ -5,6 +5,7 @@ import {
QueryClientProvider, QueryClientProvider,
} from "@tanstack/react-query" } from "@tanstack/react-query"
import { createRouter, RouterProvider } from "@tanstack/react-router" import { createRouter, RouterProvider } from "@tanstack/react-router"
import { MotionConfig } from "motion/react"
import { StrictMode } from "react" import { StrictMode } from "react"
import ReactDOM from "react-dom/client" import ReactDOM from "react-dom/client"
import { ApiError, OpenAPI } from "./client" import { ApiError, OpenAPI } from "./client"
@@ -42,11 +43,13 @@ declare module "@tanstack/react-router" {
ReactDOM.createRoot(document.getElementById("root")!).render( ReactDOM.createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme"> <ThemeProvider defaultTheme="system" storageKey="fluksio-ui-theme">
<MotionConfig reducedMotion="user">
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<RouterProvider router={router} /> <RouterProvider router={router} />
<Toaster richColors closeButton /> <Toaster richColors closeButton />
</QueryClientProvider> </QueryClientProvider>
</MotionConfig>
</ThemeProvider> </ThemeProvider>
</StrictMode>, </StrictMode>,
) )
+3 -3
View File
@@ -65,6 +65,7 @@ const LayoutAdminRoute = LayoutAdminRouteImport.update({
} as any) } as any)
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
'/': typeof LayoutIndexRoute
'/login': typeof LoginRoute '/login': typeof LoginRoute
'/recover-password': typeof RecoverPasswordRoute '/recover-password': typeof RecoverPasswordRoute
'/reset-password': typeof ResetPasswordRoute '/reset-password': typeof ResetPasswordRoute
@@ -72,7 +73,6 @@ export interface FileRoutesByFullPath {
'/admin': typeof LayoutAdminRoute '/admin': typeof LayoutAdminRoute
'/items': typeof LayoutItemsRoute '/items': typeof LayoutItemsRoute
'/settings': typeof LayoutSettingsRoute '/settings': typeof LayoutSettingsRoute
'/': typeof LayoutIndexRoute
} }
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/login': typeof LoginRoute '/login': typeof LoginRoute
@@ -99,6 +99,7 @@ export interface FileRoutesById {
export interface FileRouteTypes { export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: fullPaths:
| '/'
| '/login' | '/login'
| '/recover-password' | '/recover-password'
| '/reset-password' | '/reset-password'
@@ -106,7 +107,6 @@ export interface FileRouteTypes {
| '/admin' | '/admin'
| '/items' | '/items'
| '/settings' | '/settings'
| '/'
fileRoutesByTo: FileRoutesByTo fileRoutesByTo: FileRoutesByTo
to: to:
| '/login' | '/login'
@@ -171,7 +171,7 @@ declare module '@tanstack/react-router' {
'/_layout': { '/_layout': {
id: '/_layout' id: '/_layout'
path: '' path: ''
fullPath: '' fullPath: '/'
preLoaderRoute: typeof LayoutRouteImport preLoaderRoute: typeof LayoutRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
+2 -2
View File
@@ -22,9 +22,9 @@ export const Route = createFileRoute("/_layout")({
function Layout() { function Layout() {
return ( return (
<SidebarProvider> <SidebarProvider className="bg-card">
<AppSidebar /> <AppSidebar />
<SidebarInset> <SidebarInset className="bg-card">
<header className="sticky top-0 z-10 flex h-16 shrink-0 items-center gap-2 border-b px-4"> <header className="sticky top-0 z-10 flex h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger className="-ml-1 text-muted-foreground" /> <SidebarTrigger className="-ml-1 text-muted-foreground" />
</header> </header>
+1 -1
View File
@@ -29,7 +29,7 @@ export const Route = createFileRoute("/_layout/admin")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Admin - FastAPI Cloud", title: "Admin - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -7,7 +7,7 @@ export const Route = createFileRoute("/_layout/")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Dashboard - FastAPI Cloud", title: "Dashboard - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -21,7 +21,7 @@ export const Route = createFileRoute("/_layout/items")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Items - FastAPI Cloud", title: "Items - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -17,7 +17,7 @@ export const Route = createFileRoute("/_layout/settings")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Settings - FastAPI Cloud", title: "Settings - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -44,7 +44,7 @@ export const Route = createFileRoute("/login")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Log In - FastAPI Cloud", title: "Log In - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -42,7 +42,7 @@ export const Route = createFileRoute("/recover-password")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Recover Password - FastAPI Cloud", title: "Recover Password - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -60,7 +60,7 @@ export const Route = createFileRoute("/reset-password")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Reset Password - FastAPI Cloud", title: "Reset Password - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -51,7 +51,7 @@ export const Route = createFileRoute("/signup")({
head: () => ({ head: () => ({
meta: [ meta: [
{ {
title: "Sign Up - FastAPI Cloud", title: "Sign Up - Fluksio",
}, },
], ],
}), }),
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "fastapi-full-stack-template", "name": "fluksio",
"private": true, "private": true,
"workspaces": [ "workspaces": [
"frontend" "frontend"