From 73652675e4a1182d75376110cd9b861030e61022 Mon Sep 17 00:00:00 2001 From: Sectr99 Date: Mon, 15 Jun 2026 18:52:46 -0500 Subject: [PATCH] Add Tailwind CSS v4 + daisyUI v5 build pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a Tailwind CSS build step (no framework change — still vanilla HTML/JS). daisyUI provides shadcn-style components; a custom tokenforge theme maps the existing stone/torch color palette to daisyUI CSS variables. - css/input.css: Tailwind v4 source with @plugin "daisyui", tokenforge theme vars, and custom CSS for complex fantasy visual effects - index.html: rebuilt with Tailwind utility classes + daisyUI range, checkbox, btn, and badge components; all JS-referenced IDs and class names preserved so app.js needs no changes - package.json: build scripts — npm run dev (watch) / npm run build:css - wrangler.jsonc: build command so Cloudflare Pages compiles CSS on deploy - .gitignore: added node_modules/ To activate: install Node.js, then run npm install && npm run build:css (css/main.css is overwritten with the compiled Tailwind output). Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 3 + css/input.css | 451 +++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 203 ++++++++++++++-------- package.json | 14 ++ wrangler.jsonc | 6 +- 5 files changed, 603 insertions(+), 74 deletions(-) create mode 100644 css/input.css create mode 100644 package.json diff --git a/.gitignore b/.gitignore index a011b10..7b8cdea 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ !.dev.vars.example .env* !.env.example + +# npm +node_modules/ diff --git a/css/input.css b/css/input.css new file mode 100644 index 0000000..3cfa7f1 --- /dev/null +++ b/css/input.css @@ -0,0 +1,451 @@ +@import "tailwindcss"; +@plugin "daisyui" { + themes: false; +} + +/* Scan project files for Tailwind class usage */ +@source "../index.html"; +@source "../js"; + +/* ── Color tokens (also used in JS inline styles) ── */ +:root { + --stone-darkest: #0a0908; + --stone-dark: #131110; + --stone-mid: #1c1917; + --stone-lift: #252220; + --stone-edge: #302c28; + --stone-face: #3d3830; + --stone-pale: #5a5248; + --torch-amber: #c87820; + --torch-gold: #e0a030; + --torch-bright: #f0c060; + --torch-dim: #6a3c08; + --torch-glow: rgba(200,120,32,0.15); + --text-primary: #c8bfa8; + --text-dim: #7a7060; + --text-faint: #4a4438; +} + +/* ── daisyUI tokenforge theme ── */ +[data-theme="tokenforge"] { + color-scheme: dark; + --color-base-100: #0a0908; + --color-base-200: #131110; + --color-base-300: #1e1c18; + --color-base-content: #c8bfa8; + --color-primary: #c87820; + --color-primary-content: #0a0600; + --color-secondary: #e0a030; + --color-secondary-content: #0a0600; + --color-accent: #f0c060; + --color-accent-content: #0a0600; + --color-neutral: #2a2820; + --color-neutral-content: #7a7060; + --color-info: #38bdf8; + --color-success: #4ade80; + --color-warning: #fb923c; + --color-error: #f87171; + font-family: 'Inter', sans-serif; +} + +*, *::before, *::after { box-sizing: border-box; } +html, body { height: 100%; overflow-x: hidden; } + +body { + background: var(--stone-darkest); + color: var(--text-primary); + font-family: 'Inter', sans-serif; + position: relative; +} + +/* ── Background canvas & overlay ── */ +#bg-canvas { + position: fixed; + inset: 0; + width: 100%; height: 100%; + pointer-events: none; + z-index: 0; +} + +body::after { + content: ''; + position: fixed; + inset: 0; + background: radial-gradient(ellipse 80% 70% at 50% 40%, transparent 20%, rgba(5,4,3,0.88) 100%); + pointer-events: none; + z-index: 1; +} + +/* ── Header ── */ +.tf-header { + position: relative; + z-index: 10; + width: 100%; + background: linear-gradient(180deg, rgba(8,7,6,0.98) 0%, rgba(10,9,8,0.92) 100%); + backdrop-filter: blur(8px); + border-bottom: 1px solid var(--stone-edge); + box-shadow: 0 4px 24px rgba(0,0,0,0.7), 0 1px 0 rgba(200,120,32,0.12); +} + +.tf-header-inner { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 20px 32px 18px; + display: flex; + align-items: center; + gap: 14px; + justify-content: center; +} + +@keyframes flicker { + 0%,100% { filter: drop-shadow(0 0 6px #c87820) brightness(1); } + 15% { filter: drop-shadow(0 0 12px #e0a030) brightness(1.15); } + 30% { filter: drop-shadow(0 0 4px #c87820) brightness(0.92); } + 50% { filter: drop-shadow(0 0 10px #e0a030) brightness(1.08); } + 70% { filter: drop-shadow(0 0 5px #c87820) brightness(0.95); } +} + +.header-torch { + font-size: 26px; + animation: flicker 3s ease-in-out infinite; + flex-shrink: 0; +} + +.logo-title { + font-family: 'Cinzel', serif; + font-size: 22px; + font-weight: 700; + letter-spacing: 0.1em; + color: var(--torch-gold); + text-shadow: 0 0 20px rgba(200,120,32,0.5), 0 1px 0 rgba(0,0,0,0.8); + line-height: 1; +} + +.logo-sub { + font-size: 10px; + letter-spacing: 0.26em; + color: var(--text-dim); + text-transform: uppercase; + margin-top: 3px; +} + +.header-rule { + width: 100%; + height: 3px; + background: repeating-linear-gradient( + 90deg, + transparent 0px, transparent 2px, + rgba(200,120,32,0.08) 2px, rgba(200,120,32,0.08) 4px + ); +} + +/* ── Panels ── */ +.tf-panel { + background: + radial-gradient(ellipse 60% 40% at 0% 0%, rgba(40,55,20,0.18) 0%, transparent 60%), + linear-gradient(170deg, #1e1b17 0%, #161310 50%, #191613 100%); + border: 1px solid var(--stone-edge); + border-radius: 2px; + padding: 18px 20px 20px; + margin-bottom: 14px; + position: relative; + box-shadow: + inset 0 1px 0 rgba(255,255,255,0.04), + inset 0 -1px 0 rgba(0,0,0,0.5), + inset 2px 0 0 rgba(0,0,0,0.3), + 0 6px 24px rgba(0,0,0,0.6); +} + +.tf-panel::before { + content: ''; + position: absolute; + top: 0; left: 0; right: 0; + height: 2px; + background: linear-gradient(90deg, + transparent 0%, var(--torch-dim) 15%, + var(--torch-amber) 40%, var(--torch-dim) 70%, transparent 100%); + opacity: 0.5; + border-radius: 2px 2px 0 0; +} + +.tf-section-head { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 14px; + padding-bottom: 10px; + border-bottom: 1px solid var(--stone-edge); +} + +.section-title { + font-family: 'Cinzel', serif; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.18em; + color: var(--torch-gold); + text-transform: uppercase; + flex: 1; +} + +.section-icon { + font-size: 13px; + opacity: 0.55; + animation: flicker 3s ease-in-out infinite; + animation-delay: var(--flicker-delay, 0s); +} + +/* ── Drop zone ── */ +#drop-zone { + border: 1px dashed var(--stone-face); + border-radius: 2px; + padding: 28px 20px; + text-align: center; + cursor: pointer; + background: + repeating-linear-gradient(45deg, + rgba(0,0,0,0.15) 0px, rgba(0,0,0,0.15) 1px, + transparent 1px, transparent 8px), + rgba(8,7,6,0.5); + transition: border-color 0.2s, background 0.2s, box-shadow 0.2s; +} + +#drop-zone:hover, #drop-zone.over { + border-color: var(--torch-amber); + background: rgba(14,10,6,0.7); + box-shadow: 0 0 18px rgba(200,120,32,0.1), inset 0 0 16px rgba(200,120,32,0.04); +} + +/* ── Border grid (elements created by JS — keep these class names) ── */ +.border-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 8px; + margin-bottom: 12px; +} + +.border-opt { + cursor: pointer; + border-radius: 2px; + border: 1px solid var(--stone-edge); + background: rgba(40,35,28,0.9); + padding: 8px 6px 6px; + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + transition: border-color 0.15s, background 0.15s, box-shadow 0.15s, transform 0.1s; +} + +.border-opt:hover { + border-color: var(--torch-dim); + background: rgba(55,45,32,0.95); + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(0,0,0,0.5); +} + +.border-opt.sel { + border-color: var(--torch-amber); + background: rgba(50,32,10,0.95); + box-shadow: 0 0 10px var(--torch-glow), inset 0 0 6px rgba(200,120,32,0.08); +} + +.border-opt.loading img { opacity: 0.2; } + +.border-opt img { + width: 60px; height: 60px; + object-fit: contain; + border-radius: 50%; + background: rgba(70,60,45,0.6); + transition: opacity 0.2s; +} + +.border-opt .bname { + font-family: 'Cinzel', serif; + font-size: 9px; + letter-spacing: 0.03em; + color: var(--text-dim); + text-align: center; + line-height: 1.3; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.border-opt.sel .bname { color: var(--torch-gold); } + +/* ── Checkbox + toggle rows ── */ +.tf-link-row { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; + padding: 7px 10px; + background: rgba(6,5,4,0.5); + border-radius: 2px; + border: 1px solid var(--stone-edge); +} + +/* ── Preview frame ── */ +.preview-frame { + width: 100%; + position: relative; + padding: 10px; + background: + radial-gradient(ellipse at 50% 0%, rgba(200,120,32,0.06) 0%, transparent 60%), + linear-gradient(160deg, #1a1714 0%, #0e0c0a 100%); + border: 1px solid var(--stone-edge); + border-radius: 2px; + box-shadow: + inset 0 2px 0 rgba(0,0,0,0.5), + inset 0 0 30px rgba(0,0,0,0.4), + 0 0 0 1px rgba(200,120,32,0.06), + 0 8px 32px rgba(0,0,0,0.6); +} + +.preview-frame::before { + content: ''; + position: absolute; + top: 0; left: 10%; right: 10%; + height: 1px; + background: linear-gradient(90deg, transparent, var(--torch-amber), transparent); + opacity: 0.4; +} + +.bolt { + position: absolute; + width: 8px; height: 8px; + border-radius: 50%; + background: radial-gradient(circle at 35% 35%, var(--stone-face), var(--stone-dark)); + border: 1px solid var(--stone-edge); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.08); + z-index: 1; +} +.bolt-tl { top: 5px; left: 5px; } +.bolt-tr { top: 5px; right: 5px; } +.bolt-bl { bottom: 5px; left: 5px; } +.bolt-br { bottom: 5px; right: 5px; } + +.checker { + width: 100%; + aspect-ratio: 1; + background: repeating-conic-gradient(#100e0c 0% 25%, #0c0a08 0% 50%) 0 0 / 14px 14px; + border-radius: 1px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + position: relative; + box-shadow: inset 0 0 40px rgba(0,0,0,0.7); +} + +.checker::after { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient(ellipse at 50% 100%, rgba(200,120,32,0.08) 0%, transparent 65%); + pointer-events: none; + z-index: 2; +} + +#preview-canvas { + position: absolute; + top: 0; left: 0; + width: 100%; height: 100%; + cursor: grab; + z-index: 1; +} +#preview-canvas.dragging { cursor: grabbing; } + +.no-img { + color: var(--text-faint); + font-size: 12px; + text-align: center; + padding: 24px; + line-height: 1.8; + pointer-events: none; + z-index: 1; +} + +/* ── Safe area ring ── */ +#safe-ring { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%, -50%); + border-radius: 50%; + border: 2px dashed rgba(200,120,32,0.6); + pointer-events: none; + z-index: 3; + transition: width 0.08s, height 0.08s; + box-shadow: 0 0 8px rgba(200,120,32,0.3); + display: none; +} + +/* ── Export button (custom gold gradient, not a daisyUI btn) ── */ +#export-btn { + font-family: 'Cinzel', serif; + font-size: 14px; + font-weight: 700; + letter-spacing: 0.12em; + color: #0a0600; + background: linear-gradient(160deg, var(--torch-dim) 0%, var(--torch-amber) 35%, var(--torch-gold) 55%, var(--torch-bright) 75%, var(--torch-amber) 100%); + border: none; + border-radius: 2px; + padding: 14px 24px; + cursor: pointer; + width: 100%; + max-width: 420px; + text-transform: uppercase; + position: relative; + overflow: hidden; + transition: transform 0.1s, box-shadow 0.2s; + box-shadow: + 0 1px 0 rgba(0,0,0,0.6), + 0 4px 18px rgba(200,120,32,0.35), + inset 0 1px 0 rgba(255,220,100,0.3); + text-shadow: 0 1px 0 rgba(0,0,0,0.4); +} + +#export-btn::before { + content: ''; + position: absolute; + top: 0; left: -80%; + width: 50%; height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); + transform: skewX(-15deg); + transition: left 0.5s ease; +} +#export-btn:hover:not(:disabled)::before { left: 150%; } +#export-btn:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: 0 4px 20px rgba(200,120,32,0.4), inset 0 1px 0 rgba(255,220,100,0.3); +} +#export-btn:active:not(:disabled) { transform: translateY(0); } +#export-btn:disabled { opacity: 0.25; cursor: not-allowed; } + +/* ── Footer ── */ +.tf-footer { + position: relative; + z-index: 10; + width: 100%; + border-top: 1px solid var(--stone-edge); + background: linear-gradient(180deg, rgba(8,7,6,0.95) 0%, rgba(5,4,3,0.98) 100%); + backdrop-filter: blur(8px); +} + +.tf-footer-heading { + font-family: 'Cinzel', serif; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.18em; + color: var(--torch-gold); + text-transform: uppercase; + margin-bottom: 10px; +} + +/* ── Scrollbar ── */ +::-webkit-scrollbar { width: 6px; } +::-webkit-scrollbar-track { background: var(--stone-darkest); } +::-webkit-scrollbar-thumb { background: var(--stone-edge); border-radius: 1px; } +::-webkit-scrollbar-thumb:hover { background: var(--torch-dim); } diff --git a/index.html b/index.html index c12c454..f027206 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + @@ -8,15 +8,14 @@ - + -
-
- +
+
🔥 -
+
Token Forge
VTT Token Maker
@@ -25,41 +24,46 @@
-
+
-
+
-
-
+
+
🕯 Portrait
-
🖼
-

- Drop image here
+

🖼
+

+ Drop image here
or click to browse
- PNG · JPG · WEBP + PNG · JPG · WEBP

- +
-
-
+
+
🕯 Border
- - + +
-
+
@@ -71,57 +75,89 @@
-

Drag to reposition · Scroll to zoom

-
- 512 × 512 - PNG - Alpha + +

+ Drag to reposition · Scroll to zoom +

+ +
+ 512 × 512 + PNG + Alpha
- + -

Transparent · Roll20 · Foundry · OwlBear

+ +

+ Transparent · Roll20 · Foundry · OwlBear +

-
+
-
-
+
+
🕯 Scale & Position
-
- - - 100% + +
+ + + 100%
-
- - - 100% + +
+ + + 100%
- -
-
+
+
🕯 Safe Area
-
- - - 200 + +
+ + + 200
- @@ -130,33 +166,54 @@
-