app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 98bf329f53dd513ebaacb0befdbd2e95cd618428
parent ff300f175ca59e0acdfae4cd92853b3ca9b6a9df
Author: triesap <triesap@radroots.dev>
Date:   Thu, 22 Jan 2026 15:53:48 +0000

app: migrate tailwind pipeline to v4

- move tailwind input to `app/app.css` with v4 @import/@source directives
- relocate list styles into `crates/ui-components/assets` and wire in index
- add Trunk tailwind tool pin and remove legacy config/package files
- drop bundled assets css in favor of canonical app root stylesheet

Diffstat:
Aapp/Trunk.toml | 2++
Aapp/app.css | 414+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dapp/assets/styles.css | 413-------------------------------------------------------------------------------
Mapp/index.html | 5+++--
Dapp/package.json | 10----------
Dapp/postcss.config.js | 7-------
Dapp/tailwind.config.js | 7-------
Rapp/assets/list.css -> crates/ui-components/assets/list.css | 0
Mcrates/ui-tokens/assets/tokens.css | 3++-
9 files changed, 421 insertions(+), 440 deletions(-)

diff --git a/app/Trunk.toml b/app/Trunk.toml @@ -0,0 +1,2 @@ +[tools] +tailwindcss = "4.0.0" diff --git a/app/app.css b/app/app.css @@ -0,0 +1,414 @@ +@import "tailwindcss"; +@import "../crates/ui-tokens/assets/tokens.css"; +@import "../crates/ui-components/assets/list.css"; + +@source "./index.html"; +@source "./src/**/*.rs"; +@source "../crates/**/*.rs"; + +@layer base { + html { + font-family: var(--font-sans); + background: var(--bg-app); + color: var(--text-primary); + } + + body { + min-height: 100dvh; + margin: 0; + background: var(--bg-app); + } + + * { + -webkit-tap-highlight-color: transparent; + } + + html[data-input="keyboard"] :focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 8px; + } + + html[data-input="pointer"] :focus { + outline: none; + } + + [data-disabled] { + opacity: 0.5; + pointer-events: none; + } + + button:focus, + select:focus { + outline: none; + } + + select { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background: transparent; + background-image: none; + } + + select::-ms-expand { + display: none; + } +} + +@layer components { + .ui-surface { + background: var(--bg-elevated); + color: var(--text-primary); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-1); + } + + .ui-card { + background: var(--bg-elevated); + color: var(--text-primary); + border-radius: var(--radius-md); + box-shadow: var(--shadow-1); + } + + .ui-separator { + height: 1px; + background: var(--separator); + } + + .ui-text-secondary { + color: var(--text-secondary); + } + + .ui-text-tertiary { + color: var(--text-tertiary); + } + + .ui-material-regular { + background: var(--material-regular); + backdrop-filter: blur(18px) saturate(180%); + } + + .scroll-hide::-webkit-scrollbar { + display: none; + } + + .scroll-hide { + -ms-overflow-style: none; + scrollbar-width: none; + } + + .fade-in { + opacity: 0; + animation: fade-in 250ms ease-in-out forwards; + } + + .fade-in-long { + opacity: 0; + animation: fade-in 350ms ease-in-out forwards; + } + + .pre-wrap-text { + white-space: pre-wrap; + } + + .flex-fluid { + width: 100%; + height: 100%; + flex: 1 0 100%; + } + + .rounded-touch { + border-radius: var(--radius-xl); + } + + .h-line { + height: var(--size-line); + min-height: var(--size-line); + } + + .h-line_button { + height: var(--size-line-button); + min-height: var(--size-line-button); + } + + .carousel-container { + display: flex; + flex-grow: 1; + overflow-x: hidden; + scroll-snap-type: x mandatory; + list-style: none; + scroll-behavior: smooth; + -webkit-overflow-scrolling: touch; + } + + .carousel-item { + scroll-snap-align: start; + } + + .carousel-container-trellis { + display: flex; + flex-grow: 1; + height: 100%; + width: 100%; + } + + .carousel-item-trellis { + display: flex; + flex-direction: column; + width: fit-content; + padding: 0 var(--space-4); + gap: var(--space-4); + justify-content: flex-start; + align-items: center; + } + + .entry-line-fluid { + width: 100%; + height: 100%; + border-radius: var(--radius-xl); + } + + .entry-textarea-wrap { + display: flex; + flex-direction: row; + width: 100%; + align-items: center; + border-radius: var(--radius-xl); + } + + .entry-line-wrap { + display: flex; + flex-direction: row; + width: 100%; + padding: 0 var(--space-2); + align-items: center; + } + + .button-base { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + font: var(--type-subheadline); + text-transform: lowercase; + transition: all var(--dur-2) var(--ease-ios); + user-select: none; + cursor: pointer; + } + + .button-simple { + height: var(--size-line-button); + background: var(--bg-elevated); + color: var(--text-primary); + } + + .button-submit { + height: var(--size-line-button); + min-width: 82px; + border-radius: var(--radius-xl); + background: var(--accent); + color: var(--accent-contrast); + } + + .spinner8 { + position: relative; + display: inline-block; + width: 1em; + height: 1em; + } + + .spinner8.center { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + margin: auto; + } + + .spinner8 .spinner8-blade { + position: absolute; + left: 0.4629em; + bottom: 0; + width: 0.1em; + height: 0.2777em; + border-radius: 0.0555em; + background-color: transparent; + transform-origin: center -0.2222em; + animation: spinner-fade-base 1s infinite linear; + } + + .spinner8-white .spinner8-blade-white { + position: absolute; + left: 0.4629em; + bottom: 0; + width: 0.1em; + height: 0.2777em; + border-radius: 0.0555em; + background-color: transparent; + transform-origin: center -0.2222em; + animation: spinner-fade-white 1s infinite linear; + } + + .spinner8 .spinner8-blade:nth-child(1) { + animation-delay: 0s; + transform: rotate(0deg); + } + + .spinner8 .spinner8-blade:nth-child(2) { + animation-delay: 0.125s; + transform: rotate(45deg); + } + + .spinner8 .spinner8-blade:nth-child(3) { + animation-delay: 0.25s; + transform: rotate(90deg); + } + + .spinner8 .spinner8-blade:nth-child(4) { + animation-delay: 0.375s; + transform: rotate(135deg); + } + + .spinner8 .spinner8-blade:nth-child(5) { + animation-delay: 0.5s; + transform: rotate(180deg); + } + + .spinner8 .spinner8-blade:nth-child(6) { + animation-delay: 0.625s; + transform: rotate(225deg); + } + + .spinner8 .spinner8-blade:nth-child(7) { + animation-delay: 0.75s; + transform: rotate(270deg); + } + + .spinner8 .spinner8-blade:nth-child(8) { + animation-delay: 0.875s; + transform: rotate(315deg); + } + + [data-ui="dialog-overlay"], + [data-ui="sheet-overlay"] { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.32); + opacity: 0; + animation: overlay-fade-out 200ms ease both; + } + + [data-ui="dialog-overlay"][data-state="open"], + [data-ui="sheet-overlay"][data-state="open"] { + opacity: 1; + animation: overlay-fade-in 240ms var(--ease-ios) both; + } + + [data-ui="sheet"] { + position: fixed; + left: 0; + right: 0; + bottom: 0; + margin: 0 auto; + padding: 16px; + padding-bottom: calc(16px + var(--safe-b)); + border-top-left-radius: var(--radius-xl); + border-top-right-radius: var(--radius-xl); + background: var(--material-regular); + backdrop-filter: blur(18px) saturate(180%); + box-shadow: var(--shadow-sheet); + transform: translateY(110%); + opacity: 0; + animation: sheet-slide-out 260ms ease both; + will-change: transform, opacity; + overscroll-behavior: none; + } + + [data-ui="sheet"][data-state="open"] { + transform: translateY(0); + opacity: 1; + animation: sheet-slide-in 420ms var(--ease-ios) both; + } + + [data-ui="sheet-handle"] { + width: 36px; + height: 4px; + margin: 0 auto 12px; + border-radius: 999px; + background: var(--separator); + } + +} + +@keyframes overlay-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes overlay-fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes spinner-fade-white { + 0% { + background-color: #ffffff; + } + 100% { + background-color: transparent; + } +} + +@keyframes spinner-fade-base { + 0% { + background-color: var(--text-secondary); + } + 100% { + background-color: transparent; + } +} + +@keyframes sheet-slide-in { + 0% { + transform: translateY(110%); + opacity: 0; + } + 60% { + transform: translateY(-2%); + opacity: 1; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes sheet-slide-out { + 0% { + transform: translateY(0); + opacity: 1; + } + 100% { + transform: translateY(110%); + opacity: 0; + } +} diff --git a/app/assets/styles.css b/app/assets/styles.css @@ -1,413 +0,0 @@ -@import "../../crates/ui-tokens/assets/tokens.css"; -@import "./list.css"; - -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - html { - font-family: var(--font-sans); - background: var(--bg-app); - color: var(--text-primary); - } - - body { - min-height: 100dvh; - margin: 0; - background: var(--bg-app); - } - - * { - -webkit-tap-highlight-color: transparent; - } - - html[data-input="keyboard"] :focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - border-radius: 8px; - } - - html[data-input="pointer"] :focus { - outline: none; - } - - [data-disabled] { - opacity: 0.5; - pointer-events: none; - } - - button:focus, - select:focus { - outline: none; - } - - select { - appearance: none; - -webkit-appearance: none; - -moz-appearance: none; - background: transparent; - background-image: none; - } - - select::-ms-expand { - display: none; - } -} - -@layer components { - .ui-surface { - background: var(--bg-elevated); - color: var(--text-primary); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-1); - } - - .ui-card { - background: var(--bg-elevated); - color: var(--text-primary); - border-radius: var(--radius-md); - box-shadow: var(--shadow-1); - } - - .ui-separator { - height: 1px; - background: var(--separator); - } - - .ui-text-secondary { - color: var(--text-secondary); - } - - .ui-text-tertiary { - color: var(--text-tertiary); - } - - .ui-material-regular { - background: var(--material-regular); - backdrop-filter: blur(18px) saturate(180%); - } - - .scroll-hide::-webkit-scrollbar { - display: none; - } - - .scroll-hide { - -ms-overflow-style: none; - scrollbar-width: none; - } - - .fade-in { - opacity: 0; - animation: fade-in 250ms ease-in-out forwards; - } - - .fade-in-long { - opacity: 0; - animation: fade-in 350ms ease-in-out forwards; - } - - .pre-wrap-text { - white-space: pre-wrap; - } - - .flex-fluid { - width: 100%; - height: 100%; - flex: 1 0 100%; - } - - .rounded-touch { - border-radius: var(--radius-xl); - } - - .h-line { - height: var(--size-line); - min-height: var(--size-line); - } - - .h-line_button { - height: var(--size-line-button); - min-height: var(--size-line-button); - } - - .carousel-container { - display: flex; - flex-grow: 1; - overflow-x: hidden; - scroll-snap-type: x mandatory; - list-style: none; - scroll-behavior: smooth; - -webkit-overflow-scrolling: touch; - } - - .carousel-item { - scroll-snap-align: start; - } - - .carousel-container-trellis { - display: flex; - flex-grow: 1; - height: 100%; - width: 100%; - } - - .carousel-item-trellis { - display: flex; - flex-direction: column; - width: fit-content; - padding: 0 var(--space-4); - gap: var(--space-4); - justify-content: flex-start; - align-items: center; - } - - .entry-line-fluid { - width: 100%; - height: 100%; - border-radius: var(--radius-xl); - } - - .entry-textarea-wrap { - display: flex; - flex-direction: row; - width: 100%; - align-items: center; - border-radius: var(--radius-xl); - } - - .entry-line-wrap { - display: flex; - flex-direction: row; - width: 100%; - padding: 0 var(--space-2); - align-items: center; - } - - .button-base { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - font: var(--type-subheadline); - text-transform: lowercase; - transition: all var(--dur-2) var(--ease-ios); - user-select: none; - cursor: pointer; - } - - .button-simple { - height: var(--size-line-button); - background: var(--bg-elevated); - color: var(--text-primary); - } - - .button-submit { - height: var(--size-line-button); - min-width: 82px; - border-radius: var(--radius-xl); - background: var(--accent); - color: var(--accent-contrast); - } - - .spinner8 { - position: relative; - display: inline-block; - width: 1em; - height: 1em; - } - - .spinner8.center { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - margin: auto; - } - - .spinner8 .spinner8-blade { - position: absolute; - left: 0.4629em; - bottom: 0; - width: 0.1em; - height: 0.2777em; - border-radius: 0.0555em; - background-color: transparent; - transform-origin: center -0.2222em; - animation: spinner-fade-base 1s infinite linear; - } - - .spinner8-white .spinner8-blade-white { - position: absolute; - left: 0.4629em; - bottom: 0; - width: 0.1em; - height: 0.2777em; - border-radius: 0.0555em; - background-color: transparent; - transform-origin: center -0.2222em; - animation: spinner-fade-white 1s infinite linear; - } - - .spinner8 .spinner8-blade:nth-child(1) { - animation-delay: 0s; - transform: rotate(0deg); - } - - .spinner8 .spinner8-blade:nth-child(2) { - animation-delay: 0.125s; - transform: rotate(45deg); - } - - .spinner8 .spinner8-blade:nth-child(3) { - animation-delay: 0.25s; - transform: rotate(90deg); - } - - .spinner8 .spinner8-blade:nth-child(4) { - animation-delay: 0.375s; - transform: rotate(135deg); - } - - .spinner8 .spinner8-blade:nth-child(5) { - animation-delay: 0.5s; - transform: rotate(180deg); - } - - .spinner8 .spinner8-blade:nth-child(6) { - animation-delay: 0.625s; - transform: rotate(225deg); - } - - .spinner8 .spinner8-blade:nth-child(7) { - animation-delay: 0.75s; - transform: rotate(270deg); - } - - .spinner8 .spinner8-blade:nth-child(8) { - animation-delay: 0.875s; - transform: rotate(315deg); - } - - [data-ui="dialog-overlay"], - [data-ui="sheet-overlay"] { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.32); - opacity: 0; - animation: overlay-fade-out 200ms ease both; - } - - [data-ui="dialog-overlay"][data-state="open"], - [data-ui="sheet-overlay"][data-state="open"] { - opacity: 1; - animation: overlay-fade-in 240ms var(--ease-ios) both; - } - - [data-ui="sheet"] { - position: fixed; - left: 0; - right: 0; - bottom: 0; - margin: 0 auto; - padding: 16px; - padding-bottom: calc(16px + var(--safe-b)); - border-top-left-radius: var(--radius-xl); - border-top-right-radius: var(--radius-xl); - background: var(--material-regular); - backdrop-filter: blur(18px) saturate(180%); - box-shadow: var(--shadow-sheet); - transform: translateY(110%); - opacity: 0; - animation: sheet-slide-out 260ms ease both; - will-change: transform, opacity; - overscroll-behavior: none; - } - - [data-ui="sheet"][data-state="open"] { - transform: translateY(0); - opacity: 1; - animation: sheet-slide-in 420ms var(--ease-ios) both; - } - - [data-ui="sheet-handle"] { - width: 36px; - height: 4px; - margin: 0 auto 12px; - border-radius: 999px; - background: var(--separator); - } - -} - -@keyframes overlay-fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes overlay-fade-out { - from { - opacity: 1; - } - to { - opacity: 0; - } -} - -@keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes spinner-fade-white { - 0% { - background-color: #ffffff; - } - 100% { - background-color: transparent; - } -} - -@keyframes spinner-fade-base { - 0% { - background-color: var(--text-secondary); - } - 100% { - background-color: transparent; - } -} - -@keyframes sheet-slide-in { - 0% { - transform: translateY(110%); - opacity: 0; - } - 60% { - transform: translateY(-2%); - opacity: 1; - } - 100% { - transform: translateY(0); - opacity: 1; - } -} - -@keyframes sheet-slide-out { - 0% { - transform: translateY(0); - opacity: 1; - } - 100% { - transform: translateY(110%); - opacity: 0; - } -} diff --git a/app/index.html b/app/index.html @@ -4,9 +4,10 @@ <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Rad Roots</title> + <link rel="stylesheet" type="text/css" href="https://static.radroots.io/webfonts/sf-pro-display/styles.css" /> + <link rel="stylesheet" type="text/css" href="https://static.radroots.io/webfonts/sf-pro-rounded/styles.css" /> <link data-trunk rel="icon" href="assets/favicon.ico" /> - <link data-trunk rel="tailwind-css" href="assets/styles.css" /> - <link data-trunk rel="copy-file" href="assets/list.css" /> + <link data-trunk rel="tailwind-css" href="app.css" /> <link data-trunk rel="rust" diff --git a/app/package.json b/app/package.json @@ -1,10 +0,0 @@ -{ - "name": "radroots-app", - "private": true, - "devDependencies": { - "autoprefixer": "^10.4.19", - "postcss": "^8.4.38", - "postcss-import": "^16.1.0", - "tailwindcss": "^3.4.17" - } -} diff --git a/app/postcss.config.js b/app/postcss.config.js @@ -1,7 +0,0 @@ -module.exports = { - plugins: { - "postcss-import": {}, - tailwindcss: {}, - autoprefixer: {} - } -}; diff --git a/app/tailwind.config.js b/app/tailwind.config.js @@ -1,7 +0,0 @@ -module.exports = { - content: ["./index.html", "./src/**/*.rs", "../crates/**/*.rs"], - theme: { - extend: {} - }, - plugins: [] -}; diff --git a/app/assets/list.css b/crates/ui-components/assets/list.css diff --git a/crates/ui-tokens/assets/tokens.css b/crates/ui-tokens/assets/tokens.css @@ -1,5 +1,6 @@ :root { - --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + --font-sans: "SF Pro Rounded", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + --font-sansd: "SF Pro Display", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; --bg-app: #f2f2f7; --bg-grouped: #f2f2f7;