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:
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;