Feat: First Commit + Project Scaffolding + Initialize Styling
License Added
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
.output/
|
||||||
|
.vinxi/
|
||||||
|
.nitro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
.env.*
|
||||||
|
|
||||||
|
# OS / editor files
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
0
Backend/.gitkeep
Normal file
0
Backend/.gitkeep
Normal file
36
Commands/Local/dev.just
Normal file
36
Commands/Local/dev.just
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
project_root := justfile_directory()
|
||||||
|
frontend_dir := project_root + "/Frontend"
|
||||||
|
frontend_bake := project_root + "/Frontend/docker-bake.hcl"
|
||||||
|
local_compose := project_root + "/Docker/docker-compose.local.dev.yaml"
|
||||||
|
|
||||||
|
# Build the Frontend development image.
|
||||||
|
build:
|
||||||
|
cd '{{frontend_dir}}' && docker buildx bake -f '{{frontend_bake}}' dev
|
||||||
|
|
||||||
|
# Start the local development stack in the background using the current image.
|
||||||
|
up:
|
||||||
|
docker compose -f '{{local_compose}}' up -d --remove-orphans --force-recreate
|
||||||
|
|
||||||
|
# Build first, then start the local development stack in the background.
|
||||||
|
start: build
|
||||||
|
docker compose -f '{{local_compose}}' up -d --remove-orphans --force-recreate
|
||||||
|
|
||||||
|
# Alias for the main local development flow.
|
||||||
|
dev: start
|
||||||
|
|
||||||
|
# Stop and remove the local development stack.
|
||||||
|
down:
|
||||||
|
docker compose -f '{{local_compose}}' down --remove-orphans --volumes
|
||||||
|
|
||||||
|
# Rebuild the Frontend development image and recreate the stack.
|
||||||
|
rebuild:
|
||||||
|
cd '{{frontend_dir}}' && docker buildx bake -f '{{frontend_bake}}' dev
|
||||||
|
docker compose -f '{{local_compose}}' up -d --remove-orphans --force-recreate
|
||||||
|
|
||||||
|
# Follow logs for the local development stack.
|
||||||
|
logs:
|
||||||
|
docker compose -f '{{local_compose}}' logs -f
|
||||||
|
|
||||||
|
# Restart the local development stack.
|
||||||
|
restart:
|
||||||
|
docker compose -f '{{local_compose}}' restart
|
||||||
2
Commands/Local/mod.just
Normal file
2
Commands/Local/mod.just
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mod dev
|
||||||
|
mod prod
|
||||||
11
Commands/Local/prod.just
Normal file
11
Commands/Local/prod.just
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
project_root := justfile_directory()
|
||||||
|
frontend_dir := project_root + "/Frontend"
|
||||||
|
frontend_bake := project_root + "/Frontend/docker-bake.hcl"
|
||||||
|
|
||||||
|
# Build the Frontend production image locally.
|
||||||
|
build:
|
||||||
|
cd '{{frontend_dir}}' && docker buildx bake -f '{{frontend_bake}}' prod
|
||||||
|
|
||||||
|
# Rebuild the Frontend production image locally.
|
||||||
|
rebuild:
|
||||||
|
cd '{{frontend_dir}}' && docker buildx bake -f '{{frontend_bake}}' --set '*.no-cache=true' prod
|
||||||
0
Commands/Prod/.gitkeep
Normal file
0
Commands/Prod/.gitkeep
Normal file
15
Docker/docker-compose.local.dev.yaml
Normal file
15
Docker/docker-compose.local.dev.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
image: moku/work-frontend:dev
|
||||||
|
container_name: moku-work-frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ../Env/.env.local
|
||||||
|
ports:
|
||||||
|
- "3333:3333"
|
||||||
|
volumes:
|
||||||
|
- ../Frontend:/app
|
||||||
|
- moku_work_frontend_node_modules:/app/node_modules
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
moku_work_frontend_node_modules:
|
||||||
1
Docker/docker-compose.local.prod.yaml
Normal file
1
Docker/docker-compose.local.prod.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Reserved for a future local production compose stack.
|
||||||
0
Env/.gitkeep
Normal file
0
Env/.gitkeep
Normal file
5
Frontend/.dockerignore
Normal file
5
Frontend/.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.DS_Store
|
||||||
|
.git
|
||||||
|
.output
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
42
Frontend/Dockerfile
Normal file
42
Frontend/Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
FROM node:22-alpine AS base
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN corepack enable && corepack prepare pnpm@9.0.0 --activate
|
||||||
|
|
||||||
|
FROM base AS dependencies
|
||||||
|
|
||||||
|
COPY package.json pnpm-lock.yaml tsconfig.json vite.config.ts ./
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
FROM dependencies AS development
|
||||||
|
|
||||||
|
ENV NODE_ENV=development
|
||||||
|
ENV PORT=3333
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 3333
|
||||||
|
|
||||||
|
CMD ["pnpm", "dev", "--host", "0.0.0.0", "--port", "3333"]
|
||||||
|
|
||||||
|
FROM dependencies AS build
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
|
FROM base AS production
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=3333
|
||||||
|
|
||||||
|
COPY --from=build /app /app
|
||||||
|
|
||||||
|
EXPOSE 3333
|
||||||
|
|
||||||
|
CMD ["pnpm", "start", "--host", "0.0.0.0", "--port", "3333"]
|
||||||
32
Frontend/README.md
Normal file
32
Frontend/README.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# SolidStart
|
||||||
|
|
||||||
|
Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com);
|
||||||
|
|
||||||
|
## Creating a project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create a new project in the current directory
|
||||||
|
npm init solid@latest
|
||||||
|
|
||||||
|
# create a new project in my-app
|
||||||
|
npm init solid@latest my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
Once you've created a project and installed dependencies with `pnpm install`, start a development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# or start the server and open the app in a new browser tab
|
||||||
|
pnpm dev -- --open
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Solid apps are built with _presets_, which optimise your project for deployment to different environments.
|
||||||
|
|
||||||
|
By default, `pnpm build` will generate a Node app that you can run with `pnpm start`.
|
||||||
|
|
||||||
|
## This project was created with the [Solid CLI](https://github.com/solidjs-community/solid-cli)
|
||||||
48
Frontend/docker-bake.hcl
Normal file
48
Frontend/docker-bake.hcl
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
variable "REGISTRY" {
|
||||||
|
default = "registry.example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "TAG" {
|
||||||
|
default = "latest"
|
||||||
|
}
|
||||||
|
|
||||||
|
target "_app" {
|
||||||
|
context = "."
|
||||||
|
dockerfile = "Dockerfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
target "dev" {
|
||||||
|
inherits = ["_app"]
|
||||||
|
target = "development"
|
||||||
|
tags = ["moku/work-frontend:dev"]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "prod" {
|
||||||
|
inherits = ["_app"]
|
||||||
|
target = "production"
|
||||||
|
tags = ["moku/work-frontend:prod"]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "dev-image" {
|
||||||
|
inherits = ["_app"]
|
||||||
|
target = "development"
|
||||||
|
tags = ["${REGISTRY}/moku/work-frontend:dev-${TAG}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "prod-image" {
|
||||||
|
inherits = ["_app"]
|
||||||
|
target = "production"
|
||||||
|
tags = ["${REGISTRY}/moku/work-frontend:prod-${TAG}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
group "local" {
|
||||||
|
targets = ["dev", "prod"]
|
||||||
|
}
|
||||||
|
|
||||||
|
group "registry" {
|
||||||
|
targets = ["dev-image", "prod-image"]
|
||||||
|
}
|
||||||
|
|
||||||
|
group "default" {
|
||||||
|
targets = ["dev"]
|
||||||
|
}
|
||||||
31
Frontend/package.json
Normal file
31
Frontend/package.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "moku-frontend",
|
||||||
|
"type": "module",
|
||||||
|
"packageManager": "pnpm@9.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"defaults"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"start": "vite start",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@solidjs/start": "2.0.0-alpha.2",
|
||||||
|
"@solidjs/vite-plugin-nitro-2": "^0.1.0",
|
||||||
|
"postcss": "^8.5.15",
|
||||||
|
"sass": "^1.101.0",
|
||||||
|
"solid-js": "^1.9.5",
|
||||||
|
"vite": "^7.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^10.5.0",
|
||||||
|
"cssnano": "^8.0.2",
|
||||||
|
"postcss-preset-env": "^11.3.0",
|
||||||
|
"sass-embedded": "^1.100.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
6961
Frontend/pnpm-lock.yaml
generated
Normal file
6961
Frontend/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
5
Frontend/postcss.config.cjs
Normal file
5
Frontend/postcss.config.cjs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Path: Frontend/postcss.config.cjs
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [require("autoprefixer"), require("postcss-preset-env"), require("cssnano")],
|
||||||
|
};
|
||||||
BIN
Frontend/public/favicon.ico
Normal file
BIN
Frontend/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 664 B |
7
Frontend/src/app.tsx
Normal file
7
Frontend/src/app.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Path: Frontend/src/app.tsx
|
||||||
|
|
||||||
|
import "./styles/main.scss";
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return <main />;
|
||||||
|
}
|
||||||
4
Frontend/src/entry-client.tsx
Normal file
4
Frontend/src/entry-client.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// @refresh reload
|
||||||
|
import { mount, StartClient } from "@solidjs/start/client";
|
||||||
|
|
||||||
|
mount(() => <StartClient />, document.getElementById("app")!);
|
||||||
40
Frontend/src/entry-server.tsx
Normal file
40
Frontend/src/entry-server.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Path: Frontend/src/entry-server.tsx
|
||||||
|
|
||||||
|
// @refresh reload
|
||||||
|
import { createHandler, StartServer } from "@solidjs/start/server";
|
||||||
|
|
||||||
|
const themeBootstrapScript = `
|
||||||
|
(() => {
|
||||||
|
try {
|
||||||
|
const storageKey = "theme";
|
||||||
|
const stored = localStorage.getItem(storageKey);
|
||||||
|
const theme = stored === "light" || stored === "dark"
|
||||||
|
? stored
|
||||||
|
: (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
||||||
|
|
||||||
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
|
} catch {
|
||||||
|
document.documentElement.setAttribute("data-theme", "light");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default createHandler(() => (
|
||||||
|
<StartServer
|
||||||
|
document={({ assets, children, scripts }) => (
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<script innerHTML={themeBootstrapScript} />
|
||||||
|
{assets}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">{children}</div>
|
||||||
|
{scripts}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
1
Frontend/src/global.d.ts
vendored
Normal file
1
Frontend/src/global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="@solidjs/start/env" />
|
||||||
22
Frontend/src/helpers/theme.ts
Normal file
22
Frontend/src/helpers/theme.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Path: Frontend/src/helpers/theme.ts
|
||||||
|
|
||||||
|
export type Theme = "light" | "dark";
|
||||||
|
|
||||||
|
export const THEME_STORAGE_KEY = "theme";
|
||||||
|
|
||||||
|
export const resolvePreferredTheme = (): Theme => {
|
||||||
|
const stored = localStorage.getItem(THEME_STORAGE_KEY);
|
||||||
|
|
||||||
|
if (stored === "light" || stored === "dark") {
|
||||||
|
return stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDocumentTheme = (): Theme => (document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light");
|
||||||
|
|
||||||
|
export const setTheme = (theme: Theme) => {
|
||||||
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
|
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
||||||
|
};
|
||||||
6
Frontend/src/styles/base/_fonts.scss
Normal file
6
Frontend/src/styles/base/_fonts.scss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/* Path: Frontend/src/styles/base/_fonts.scss */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-family-sans: "Inter", "Geist", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-family-mono: "Geist Mono", "SFMono-Regular", ui-monospace, "Cascadia Code", "Roboto Mono", monospace;
|
||||||
|
}
|
||||||
100
Frontend/src/styles/base/_globals.scss
Normal file
100
Frontend/src/styles/base/_globals.scss
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/* Path: Frontend/src/styles/base/_globals.scss */
|
||||||
|
|
||||||
|
@use "../tools/mixins" as *;
|
||||||
|
|
||||||
|
html {
|
||||||
|
background: var(--color-canvas);
|
||||||
|
color: var(--color-text);
|
||||||
|
font-family: var(--font-family-sans);
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
transition:
|
||||||
|
background-color var(--motion-duration-base) var(--motion-ease-standard),
|
||||||
|
color var(--motion-duration-base) var(--motion-ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--color-canvas);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
transition:
|
||||||
|
background-color var(--motion-duration-fast) var(--motion-ease-standard),
|
||||||
|
border-color var(--motion-duration-fast) var(--motion-ease-standard),
|
||||||
|
color var(--motion-duration-fast) var(--motion-ease-standard),
|
||||||
|
box-shadow var(--motion-duration-fast) var(--motion-ease-standard),
|
||||||
|
transform var(--motion-duration-fast) var(--motion-ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:focus-visible {
|
||||||
|
outline: 2px solid var(--color-focus-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--color-accent-soft);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@include text-display;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@include text-heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
@include text-title;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
@include text-label;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
span,
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
label,
|
||||||
|
li {
|
||||||
|
@include text-body;
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
pre,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: var(--font-family-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
69
Frontend/src/styles/base/_reset.scss
Normal file
69
Frontend/src/styles/base/_reset.scss
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/* Path: Frontend/src/styles/base/_reset.scss */
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
picture,
|
||||||
|
video,
|
||||||
|
canvas,
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul[role="list"],
|
||||||
|
ol[role="list"] {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
9
Frontend/src/styles/main.scss
Normal file
9
Frontend/src/styles/main.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* Path: Frontend/src/styles/main.scss */
|
||||||
|
|
||||||
|
@use "./base/reset";
|
||||||
|
@use "./base/fonts";
|
||||||
|
@use "./base/globals";
|
||||||
|
|
||||||
|
@use "./themes/tokens";
|
||||||
|
@use "./themes/light";
|
||||||
|
@use "./themes/dark";
|
||||||
28
Frontend/src/styles/themes/_dark.scss
Normal file
28
Frontend/src/styles/themes/_dark.scss
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* Path: Frontend/src/styles/themes/_dark.scss */
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
|
||||||
|
--color-canvas: var(--gray-900);
|
||||||
|
--color-surface: hsl(220 23% 14% / 0.92);
|
||||||
|
--color-surface-muted: hsl(220 22% 12% / 0.96);
|
||||||
|
--color-surface-hover: hsl(220 18% 20% / 0.96);
|
||||||
|
--color-border: hsl(220 12% 26% / 0.9);
|
||||||
|
--color-border-strong: hsl(220 12% 38% / 0.9);
|
||||||
|
|
||||||
|
--color-text: hsl(210 20% 96%);
|
||||||
|
--color-text-muted: hsl(220 12% 70%);
|
||||||
|
|
||||||
|
--color-accent: hsl(217 91% 67%);
|
||||||
|
--color-accent-strong: hsl(218 88% 61%);
|
||||||
|
--color-accent-soft: hsl(217 91% 67% / 0.18);
|
||||||
|
--color-accent-contrast: hsl(220 28% 10%);
|
||||||
|
|
||||||
|
--color-success: hsl(154 55% 48%);
|
||||||
|
--color-danger: hsl(0 72% 62%);
|
||||||
|
--color-warning: hsl(36 100% 60%);
|
||||||
|
--color-focus-ring: hsl(217 91% 67% / 0.65);
|
||||||
|
|
||||||
|
--shadow-soft: 0 16px 40px hsl(220 40% 3% / 0.45);
|
||||||
|
--shadow-strong: 0 24px 60px hsl(220 40% 3% / 0.55);
|
||||||
|
}
|
||||||
26
Frontend/src/styles/themes/_light.scss
Normal file
26
Frontend/src/styles/themes/_light.scss
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/* Path: Frontend/src/styles/themes/_light.scss */
|
||||||
|
|
||||||
|
:root,
|
||||||
|
[data-theme="light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
|
||||||
|
--color-canvas: var(--gray-50);
|
||||||
|
--color-surface: hsl(0 0% 100% / 0.9);
|
||||||
|
--color-surface-muted: var(--gray-0);
|
||||||
|
--color-surface-hover: var(--gray-100);
|
||||||
|
--color-border: hsl(220 15% 85% / 0.9);
|
||||||
|
--color-border-strong: hsl(220 12% 70% / 0.9);
|
||||||
|
|
||||||
|
--color-text: var(--gray-800);
|
||||||
|
--color-text-muted: var(--gray-500);
|
||||||
|
|
||||||
|
--color-accent: var(--blue-500);
|
||||||
|
--color-accent-strong: var(--blue-600);
|
||||||
|
--color-accent-soft: hsl(218 88% 61% / 0.12);
|
||||||
|
--color-accent-contrast: hsl(0 0% 100%);
|
||||||
|
|
||||||
|
--color-success: var(--green-500);
|
||||||
|
--color-danger: var(--red-500);
|
||||||
|
--color-warning: var(--amber-500);
|
||||||
|
--color-focus-ring: hsl(221 83% 53% / 0.55);
|
||||||
|
}
|
||||||
54
Frontend/src/styles/themes/_tokens.scss
Normal file
54
Frontend/src/styles/themes/_tokens.scss
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/* Path: Frontend/src/styles/themes/_tokens.scss */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--gray-0: hsl(210 20% 99%);
|
||||||
|
--gray-50: hsl(220 20% 97%);
|
||||||
|
--gray-100: hsl(220 16% 93%);
|
||||||
|
--gray-200: hsl(220 13% 87%);
|
||||||
|
--gray-300: hsl(220 11% 75%);
|
||||||
|
--gray-400: hsl(220 9% 58%);
|
||||||
|
--gray-500: hsl(220 10% 45%);
|
||||||
|
--gray-600: hsl(220 14% 34%);
|
||||||
|
--gray-700: hsl(220 18% 24%);
|
||||||
|
--gray-800: hsl(220 22% 16%);
|
||||||
|
--gray-900: hsl(220 28% 10%);
|
||||||
|
|
||||||
|
--blue-400: hsl(218 88% 61%);
|
||||||
|
--blue-500: hsl(221 83% 53%);
|
||||||
|
--blue-600: hsl(224 76% 48%);
|
||||||
|
|
||||||
|
--green-500: hsl(154 60% 40%);
|
||||||
|
--red-500: hsl(0 72% 54%);
|
||||||
|
--amber-500: hsl(36 100% 50%);
|
||||||
|
|
||||||
|
--space-1: 0.25rem;
|
||||||
|
--space-2: 0.5rem;
|
||||||
|
--space-3: 0.75rem;
|
||||||
|
--space-4: 1rem;
|
||||||
|
--space-5: 1.25rem;
|
||||||
|
--space-6: 1.5rem;
|
||||||
|
--space-8: 2rem;
|
||||||
|
--space-10: 2.5rem;
|
||||||
|
--space-12: 3rem;
|
||||||
|
|
||||||
|
--radius-sm: 0.375rem;
|
||||||
|
--radius-md: 0.625rem;
|
||||||
|
--radius-lg: 0.875rem;
|
||||||
|
--radius-xl: 1.25rem;
|
||||||
|
--radius-pill: 999px;
|
||||||
|
|
||||||
|
--shadow-soft: 0 12px 32px hsl(220 30% 10% / 0.08);
|
||||||
|
--shadow-strong: 0 20px 48px hsl(220 30% 10% / 0.16);
|
||||||
|
|
||||||
|
--z-base: 1;
|
||||||
|
--z-dropdown: 100;
|
||||||
|
--z-sticky: 200;
|
||||||
|
--z-overlay: 400;
|
||||||
|
--z-modal: 500;
|
||||||
|
--z-toast: 600;
|
||||||
|
|
||||||
|
--motion-duration-fast: 140ms;
|
||||||
|
--motion-duration-base: 220ms;
|
||||||
|
--motion-duration-slow: 320ms;
|
||||||
|
--motion-ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||||
|
}
|
||||||
5
Frontend/src/styles/tools/_breakpoints.scss
Normal file
5
Frontend/src/styles/tools/_breakpoints.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/* Path: Frontend/src/styles/tools/_breakpoints.scss */
|
||||||
|
|
||||||
|
$breakpoint-mobile: 48rem;
|
||||||
|
$breakpoint-tablet: 64rem;
|
||||||
|
$breakpoint-desktop: 90rem;
|
||||||
5
Frontend/src/styles/tools/_functions.scss
Normal file
5
Frontend/src/styles/tools/_functions.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/* Path: Frontend/src/styles/tools/_functions.scss */
|
||||||
|
|
||||||
|
@function rem($pixels, $base: 16) {
|
||||||
|
@return ($pixels / $base) * 1rem;
|
||||||
|
}
|
||||||
55
Frontend/src/styles/tools/_mixins.scss
Normal file
55
Frontend/src/styles/tools/_mixins.scss
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/* Path: Frontend/src/styles/tools/_mixins.scss */
|
||||||
|
|
||||||
|
@use "./breakpoints" as *;
|
||||||
|
|
||||||
|
@mixin respond-up($breakpoint) {
|
||||||
|
@if $breakpoint == mobile {
|
||||||
|
@media (min-width: $breakpoint-mobile) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
} @else if $breakpoint == tablet {
|
||||||
|
@media (min-width: $breakpoint-tablet) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
} @else if $breakpoint == desktop {
|
||||||
|
@media (min-width: $breakpoint-desktop) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-caption {
|
||||||
|
font-size: clamp(0.75rem, 0.72rem + 0.12vw, 0.875rem);
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-label {
|
||||||
|
font-size: clamp(0.875rem, 0.84rem + 0.15vw, 1rem);
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-body {
|
||||||
|
font-size: clamp(0.95rem, 0.92rem + 0.18vw, 1.05rem);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-title {
|
||||||
|
font-size: clamp(1.1rem, 1rem + 0.4vw, 1.35rem);
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-heading {
|
||||||
|
font-size: clamp(1.45rem, 1.2rem + 1vw, 2rem);
|
||||||
|
font-weight: 650;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-display {
|
||||||
|
font-size: clamp(2rem, 1.45rem + 2.1vw, 3.5rem);
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
19
Frontend/tsconfig.json
Normal file
19
Frontend/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "solid-js",
|
||||||
|
"allowJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"isolatedModules": true,
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Frontend/vite.config.ts
Normal file
17
Frontend/vite.config.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Path: Frontend/vite.config.ts
|
||||||
|
|
||||||
|
import { nitroV2Plugin as nitro } from "@solidjs/vite-plugin-nitro-2";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
import { solidStart } from "@solidjs/start/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
css: {
|
||||||
|
preprocessorOptions: {
|
||||||
|
scss: {
|
||||||
|
additionalData: `@use "/src/styles/tools/functions" as *;\n@use "/src/styles/tools/breakpoints" as *;\n@use "/src/styles/tools/mixins" as *;\n`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [solidStart(), nitro()],
|
||||||
|
});
|
||||||
7
Justfile
Normal file
7
Justfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
set shell := ["bash", "-cu"]
|
||||||
|
|
||||||
|
mod local "Commands/Local"
|
||||||
|
|
||||||
|
[default]
|
||||||
|
help:
|
||||||
|
just --list --list-submodules
|
||||||
131
LICENSE
Normal file
131
LICENSE
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# PolyForm Noncommercial License 1.0.0
|
||||||
|
|
||||||
|
<https://polyformproject.org/licenses/noncommercial/1.0.0>
|
||||||
|
|
||||||
|
## Acceptance
|
||||||
|
|
||||||
|
In order to get any license under these terms, you must agree
|
||||||
|
to them as both strict obligations and conditions to all
|
||||||
|
your licenses.
|
||||||
|
|
||||||
|
## Copyright License
|
||||||
|
|
||||||
|
The licensor grants you a copyright license for the
|
||||||
|
software to do everything you might do with the software
|
||||||
|
that would otherwise infringe the licensor's copyright
|
||||||
|
in it for any permitted purpose. However, you may
|
||||||
|
only distribute the software according to [Distribution
|
||||||
|
License](#distribution-license) and make changes or new works
|
||||||
|
based on the software according to [Changes and New Works
|
||||||
|
License](#changes-and-new-works-license).
|
||||||
|
|
||||||
|
## Distribution License
|
||||||
|
|
||||||
|
The licensor grants you an additional copyright license
|
||||||
|
to distribute copies of the software. Your license
|
||||||
|
to distribute covers distributing the software with
|
||||||
|
changes and new works permitted by [Changes and New Works
|
||||||
|
License](#changes-and-new-works-license).
|
||||||
|
|
||||||
|
## Notices
|
||||||
|
|
||||||
|
You must ensure that anyone who gets a copy of any part of
|
||||||
|
the software from you also gets a copy of these terms or the
|
||||||
|
URL for them above, as well as copies of any plain-text lines
|
||||||
|
beginning with `Required Notice:` that the licensor provided
|
||||||
|
with the software. For example:
|
||||||
|
|
||||||
|
> Required Notice: Copyright Yoyodyne, Inc. (<http://example.com>)
|
||||||
|
|
||||||
|
## Changes and New Works License
|
||||||
|
|
||||||
|
The licensor grants you an additional copyright license to
|
||||||
|
make changes and new works based on the software for any
|
||||||
|
permitted purpose.
|
||||||
|
|
||||||
|
## Patent License
|
||||||
|
|
||||||
|
The licensor grants you a patent license for the software that
|
||||||
|
covers patent claims the licensor can license, or becomes able
|
||||||
|
to license, that you would infringe by using the software.
|
||||||
|
|
||||||
|
## Noncommercial Purposes
|
||||||
|
|
||||||
|
Any noncommercial purpose is a permitted purpose.
|
||||||
|
|
||||||
|
## Personal Uses
|
||||||
|
|
||||||
|
Personal use for research, experiment, and testing for
|
||||||
|
the benefit of public knowledge, personal study, private
|
||||||
|
entertainment, hobby projects, amateur pursuits, or religious
|
||||||
|
observance, without any anticipated commercial application,
|
||||||
|
is use for a permitted purpose.
|
||||||
|
|
||||||
|
## Noncommercial Organizations
|
||||||
|
|
||||||
|
Use by any charitable organization, educational institution,
|
||||||
|
public research organization, public safety or health
|
||||||
|
organization, environmental protection organization,
|
||||||
|
or government institution is use for a permitted purpose
|
||||||
|
regardless of the source of funding or obligations resulting
|
||||||
|
from the funding.
|
||||||
|
|
||||||
|
## Fair Use
|
||||||
|
|
||||||
|
You may have "fair use" rights for the software under the
|
||||||
|
law. These terms do not limit them.
|
||||||
|
|
||||||
|
## No Other Rights
|
||||||
|
|
||||||
|
These terms do not allow you to sublicense or transfer any of
|
||||||
|
your licenses to anyone else, or prevent the licensor from
|
||||||
|
granting licenses to anyone else. These terms do not imply
|
||||||
|
any other licenses.
|
||||||
|
|
||||||
|
## Patent Defense
|
||||||
|
|
||||||
|
If you make any written claim that the software infringes or
|
||||||
|
contributes to infringement of any patent, your patent license
|
||||||
|
for the software granted under these terms ends immediately. If
|
||||||
|
your company makes such a claim, your patent license ends
|
||||||
|
immediately for work on behalf of your company.
|
||||||
|
|
||||||
|
## Violations
|
||||||
|
|
||||||
|
The first time you are notified in writing that you have
|
||||||
|
violated any of these terms, or done anything with the software
|
||||||
|
not covered by your licenses, your licenses can nonetheless
|
||||||
|
continue if you come into full compliance with these terms,
|
||||||
|
and take practical steps to correct past violations, within
|
||||||
|
32 days of receiving notice. Otherwise, all your licenses
|
||||||
|
end immediately.
|
||||||
|
|
||||||
|
## No Liability
|
||||||
|
|
||||||
|
***As far as the law allows, the software comes as is, without
|
||||||
|
any warranty or condition, and the licensor will not be liable
|
||||||
|
to you for any damages arising out of these terms or the use
|
||||||
|
or nature of the software, under any kind of legal claim.***
|
||||||
|
|
||||||
|
## Definitions
|
||||||
|
|
||||||
|
The **licensor** is the individual or entity offering these
|
||||||
|
terms, and the **software** is the software the licensor makes
|
||||||
|
available under these terms.
|
||||||
|
|
||||||
|
**You** refers to the individual or entity agreeing to these
|
||||||
|
terms.
|
||||||
|
|
||||||
|
**Your company** is any legal entity, sole proprietorship,
|
||||||
|
or other kind of organization that you work for, plus all
|
||||||
|
organizations that have control over, are under the control of,
|
||||||
|
or are under common control with that organization. **Control**
|
||||||
|
means ownership of substantially all the assets of an entity,
|
||||||
|
or the power to direct its management and policies by vote,
|
||||||
|
contract, or otherwise. Control can be direct or indirect.
|
||||||
|
|
||||||
|
**Your licenses** are all the licenses granted to you for the
|
||||||
|
software under these terms.
|
||||||
|
|
||||||
|
**Use** means anything you do with the software requiring one
|
||||||
|
of your licenses.
|
||||||
0
Proxy/.gitkeep
Normal file
0
Proxy/.gitkeep
Normal file
32
README.md
Normal file
32
README.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Moku Base
|
||||||
|
|
||||||
|
Empty scaffold for a Monday-style replacement app.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
- `Backend/` - blank backend placeholder
|
||||||
|
- `Commands/` - Just submodules and command entrypoints
|
||||||
|
- `Docker/` - local compose files
|
||||||
|
- `Env/` - local environment files
|
||||||
|
- `Frontend/` - SolidStart bare workspace shell
|
||||||
|
- `Proxy/` - blank proxy placeholder
|
||||||
|
|
||||||
|
## Local usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just --list --list-submodules
|
||||||
|
```
|
||||||
|
|
||||||
|
Main local flow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just local dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the PolyForm Noncommercial License 1.0.0.
|
||||||
|
|
||||||
|
Commercial use is not permitted.
|
||||||
|
|
||||||
|
See `LICENSE` for details.
|
||||||
Reference in New Issue
Block a user