Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ash_learning-*.tar
app-*.tar

/priv/static/assets/
/priv/static/fonts/
/priv/ssr-js/
/priv/static/cache_manifest.json
/priv/plts/
Expand Down
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This is a web application written using the Phoenix web framework.

- Use `mix precommit` alias when you are done with all changes and fix any pending issues
- Use the already included and available `:req` (`Req`) library for HTTP requests, **avoid** `:httpoison`, `:tesla`, and `:httpc`. Req is included by default and is the preferred HTTP client for Phoenix apps
- **All Node.js/package.json commands must be run through `./bin/pnpm.sh COMMAND`** - This includes `lint`, `fmt`, `dev`, `build`, etc. Never run pnpm/npm commands directly in the assets directory

### Phoenix v1.8 guidelines

Expand Down Expand Up @@ -416,7 +417,7 @@ mix usage_rules.docs Enum.zip/1

## Searching Documentation

You should also consult the documentation of any tools you are using, early and often. The best
You should also consult the documentation of any tools you are using, early and often. The best
way to accomplish this is to use the `usage_rules.search_docs` mix task. Once you have
found what you are looking for, use the links in the search results to get more detail. For example:

Expand Down Expand Up @@ -996,7 +997,7 @@ mix usage_rules.docs Enum.zip/1

## Searching Documentation

You should also consult the documentation of any tools you are using, early and often. The best
You should also consult the documentation of any tools you are using, early and often. The best
way to accomplish this is to use the `usage_rules.search_docs` mix task. Once you have
found what you are looking for, use the links in the search results to get more detail. For example:

Expand Down Expand Up @@ -1570,7 +1571,7 @@ mix usage_rules.docs Enum.zip/1

## Searching Documentation

You should also consult the documentation of any tools you are using, early and often. The best
You should also consult the documentation of any tools you are using, early and often. The best
way to accomplish this is to use the `usage_rules.search_docs` mix task. Once you have
found what you are looking for, use the links in the search results to get more detail. For example:

Expand Down
1 change: 1 addition & 0 deletions assets/.oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"useTabs": false,
"semi": false,
"singleQuote": true,
"jsxSingleQuote": true,
"trailingComma": "all",
"experimentalSortImports": {
"groups": [
Expand Down
2 changes: 1 addition & 1 deletion assets/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react-refresh/only-export-components": [
"warn",
"error",
{
"allowConstantExport": true
}
Expand Down
25 changes: 25 additions & 0 deletions assets/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "css/app.css",
"baseColor": "gray",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "inverted",
"menuAccent": "subtle",
"registries": {}
}
149 changes: 149 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
@@ -1 +1,150 @@
@import 'tailwindcss';
@import 'tw-animate-css';
@import 'shadcn/tailwind.css';
@import './fonts.css';

@source "../../lib/ash_learning_web/components/layouts/root.html.heex";

@custom-variant dark (&:is(.dark *));

:root {
--background: oklch(1 0 0);
--foreground: oklch(0.13 0.028 261.692);
--card: oklch(1 0 0);
--card-foreground: oklch(0.13 0.028 261.692);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.13 0.028 261.692);
--primary: oklch(0.21 0.034 264.665);
--primary-foreground: oklch(0.985 0.002 247.839);
--secondary: oklch(0.967 0.003 264.542);
--secondary-foreground: oklch(0.21 0.034 264.665);
--muted: oklch(0.967 0.003 264.542);
--muted-foreground: oklch(0.551 0.027 264.364);
--accent: oklch(0.967 0.003 264.542);
--accent-foreground: oklch(0.21 0.034 264.665);

--destructive: oklch(93.6% 0.032 17.717); /* Red 100 */
--destructive-muted: oklch(97.1% 0.013 17.38); /* Red 50 */
--destructive-border: oklch(70.4% 0.191 22.216); /* Red 400 */
--destructive-ring: oklch(93.6% 0.032 17.717); /* Red 100 */
--destructive-foreground: oklch(50.5% 0.213 27.518); /* Red 700 */
--destructive-foreground-muted: oklch(57.7% 0.245 27.325); /* Red 600 */

--border: oklch(0.928 0.006 264.531);
--input: oklch(0.928 0.006 264.531);
--ring: oklch(0.707 0.022 261.325);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.45rem;
--sidebar: oklch(0.985 0.002 247.839);
--sidebar-foreground: oklch(0.13 0.028 261.692);
--sidebar-primary: oklch(0.21 0.034 264.665);
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
--sidebar-accent: oklch(0.967 0.003 264.542);
--sidebar-accent-foreground: oklch(0.21 0.034 264.665);
--sidebar-border: oklch(0.928 0.006 264.531);
--sidebar-ring: oklch(0.707 0.022 261.325);
}

.dark {
--background: oklch(0.13 0.028 261.692);
--foreground: oklch(0.985 0.002 247.839);
--card: oklch(0.21 0.034 264.665);
--card-foreground: oklch(0.985 0.002 247.839);
--popover: oklch(0.21 0.034 264.665);
--popover-foreground: oklch(0.985 0.002 247.839);
--primary: oklch(0.928 0.006 264.531);
--primary-foreground: oklch(0.21 0.034 264.665);
--secondary: oklch(0.278 0.033 256.848);
--secondary-foreground: oklch(0.985 0.002 247.839);
--muted: oklch(0.278 0.033 256.848);
--muted-foreground: oklch(0.707 0.022 261.325);
--accent: oklch(0.278 0.033 256.848);
--accent-foreground: oklch(0.985 0.002 247.839);

--destructive: oklch(57.7% 0.245 27.325); /* Red 600 */
--destructive-muted: oklch(50.5% 0.213 27.518); /* Red 700 */
--destructive-border: oklch(70.4% 0.191 22.216); /* Red 400 */
--destructive-ring: oklch(97.1% 0.013 17.38 / 30%); /* Red 50 */
--destructive-foreground: oklch(93.6% 0.032 17.717); /* Red 100 */
--destructive-foreground-muted: oklch(88.5% 0.062 18.334); /* Red 200 */

--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.551 0.027 264.364);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.034 264.665);
--sidebar-foreground: oklch(0.985 0.002 247.839);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
--sidebar-accent: oklch(0.278 0.033 256.848);
--sidebar-accent-foreground: oklch(0.985 0.002 247.839);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.551 0.027 264.364);
}

@theme inline {
--font-sans: 'Geist Variable', sans-serif;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-destructive-muted: var(--destructive-muted);
--color-destructive-border: var(--destructive-border);
--color-destructive-ring: var(--destructive-ring);
--color-destructive-foreground: var(--destructive-foreground);
--color-destructive-foreground-muted: var(--destructive-foreground-muted);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-background: var(--background);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
}

@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply font-sans bg-background text-foreground;
}
html {
@apply font-sans;
}
}
64 changes: 64 additions & 0 deletions assets/css/fonts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Geist Variable Font - Manual Installation
*
* Source: https://fontsource.org/fonts/geist/install
*
* This is a manual installation of the Geist Variable font.
*
* The recommended approach by Fontsource and Shadcn is to use:
* npm install @fontsource-variable/geist
* @import '@fontsource-variable/geist';
*
* However, due to development server complexities in our Phoenix + Docker setup,
* we're using manual font files copied from the npm package.
*
* Font files copied from: node_modules/@fontsource-variable/geist/files/
* CSS definitions copied from: node_modules/@fontsource-variable/geist/index.css
*
* To update fonts:
* 1. Update the @fontsource-variable/geist package
* 2. Copy new font files from node_modules/@fontsource-variable/geist/files/
* to assets/static/fonts/ and priv/static/fonts/
* 3. Update the CSS below from node_modules/@fontsource-variable/geist/index.css
*
* Note: priv/static/fonts/ is gitignored as it's a build artifact.
* The source fonts in assets/static/fonts/ are committed to git.
*
* Usage in CSS:
* font-family: "Geist Variable", sans-serif;
*/

/* geist-cyrillic-wght-normal */
@font-face {
font-family: 'Geist Variable';
font-style: normal;
font-display: swap;
font-weight: 100 900;
src: url('/fonts/geist-cyrillic-wght-normal.woff2') format('woff2-variations');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* geist-latin-ext-wght-normal */
@font-face {
font-family: 'Geist Variable';
font-style: normal;
font-display: swap;
font-weight: 100 900;
src: url('/fonts/geist-latin-ext-wght-normal.woff2') format('woff2-variations');
unicode-range:
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
U+A720-A7FF;
}

/* geist-latin-wght-normal */
@font-face {
font-family: 'Geist Variable';
font-style: normal;
font-display: swap;
font-weight: 100 900;
src: url('/fonts/geist-latin-wght-normal.woff2') format('woff2-variations');
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
2 changes: 1 addition & 1 deletion assets/js/Layouts/AppLayout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function AppLayout({
* [Read more](https://inertiajs.com/pages#scroll-regions)
*/}
<div
scroll-region="true"
scroll-region='true'
className={`@container/appLayout flex flex-1 flex-col gap-y-4 py-4 pt-0 ${APP_HORIZONTAL_PADDING}`}
>
{children}
Expand Down
Loading