mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-09 23:45:21 +02:00
Add lookbook + viewcomponent, organize design system file
This commit is contained in:
parent
210b89cd17
commit
6af50928e6
19 changed files with 656 additions and 372 deletions
4
Gemfile
4
Gemfile
|
@ -19,9 +19,11 @@ gem "propshaft"
|
|||
gem "tailwindcss-rails"
|
||||
gem "lucide-rails", github: "maybe-finance/lucide-rails"
|
||||
|
||||
# Hotwire
|
||||
# Hotwire + UI
|
||||
gem "stimulus-rails"
|
||||
gem "turbo-rails"
|
||||
gem "view_component"
|
||||
gem "lookbook", ">= 2.3.7"
|
||||
|
||||
gem "hotwire_combobox"
|
||||
|
||||
|
|
25
Gemfile.lock
25
Gemfile.lock
|
@ -139,6 +139,8 @@ GEM
|
|||
bigdecimal
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
css_parser (1.21.1)
|
||||
addressable
|
||||
csv (3.3.4)
|
||||
date (3.4.1)
|
||||
debug (1.10.0)
|
||||
|
@ -194,6 +196,8 @@ GEM
|
|||
rails (>= 7.0.7.2)
|
||||
stimulus-rails (>= 1.2)
|
||||
turbo-rails (>= 1.2)
|
||||
htmlbeautifier (1.4.3)
|
||||
htmlentities (4.3.4)
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (1.0.15)
|
||||
|
@ -255,6 +259,18 @@ GEM
|
|||
loofah (2.24.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
lookbook (2.3.9)
|
||||
activemodel
|
||||
css_parser
|
||||
htmlbeautifier (~> 1.3)
|
||||
htmlentities (~> 4.3.4)
|
||||
marcel (~> 1.0)
|
||||
railties (>= 5.0)
|
||||
redcarpet (~> 3.5)
|
||||
rouge (>= 3.26, < 5.0)
|
||||
view_component (>= 2.0)
|
||||
yard (~> 0.9)
|
||||
zeitwerk (~> 2.5)
|
||||
mail (2.8.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
|
@ -262,6 +278,7 @@ GEM
|
|||
net-smtp
|
||||
marcel (1.0.4)
|
||||
matrix (0.4.2)
|
||||
method_source (1.1.0)
|
||||
mini_magick (5.2.0)
|
||||
benchmark
|
||||
logger
|
||||
|
@ -394,6 +411,7 @@ GEM
|
|||
io-console (~> 0.5)
|
||||
rexml (3.4.1)
|
||||
rotp (6.3.0)
|
||||
rouge (4.5.1)
|
||||
rqrcode (2.2.0)
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 1.0)
|
||||
|
@ -508,6 +526,10 @@ GEM
|
|||
vcr (6.3.1)
|
||||
base64
|
||||
vernier (1.7.0)
|
||||
view_component (3.21.0)
|
||||
activesupport (>= 5.2.0, < 8.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
method_source (~> 1.0)
|
||||
web-console (4.2.1)
|
||||
actionview (>= 6.0.0)
|
||||
activemodel (>= 6.0.0)
|
||||
|
@ -524,6 +546,7 @@ GEM
|
|||
websocket-extensions (0.1.5)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
yard (0.9.37)
|
||||
zeitwerk (2.7.2)
|
||||
|
||||
PLATFORMS
|
||||
|
@ -564,6 +587,7 @@ DEPENDENCIES
|
|||
jwt
|
||||
letter_opener
|
||||
logtail-rails
|
||||
lookbook (>= 2.3.7)
|
||||
lucide-rails!
|
||||
mocha
|
||||
octokit
|
||||
|
@ -596,6 +620,7 @@ DEPENDENCIES
|
|||
tzinfo-data
|
||||
vcr
|
||||
vernier
|
||||
view_component
|
||||
web-console
|
||||
webmock
|
||||
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
One-off styling (3rd party overrides, etc.) should be done in the application.css file.
|
||||
*/
|
||||
|
||||
@import './maybe-design-system/background-utils.css';
|
||||
@import './maybe-design-system/foreground-utils.css';
|
||||
@import './maybe-design-system/text-utils.css';
|
||||
@import './maybe-design-system/border-utils.css';
|
||||
@import './maybe-design-system/component-utils.css';
|
||||
|
||||
@custom-variant theme-dark (&:where([data-theme=dark], [data-theme=dark] *));
|
||||
|
||||
@theme {
|
||||
|
@ -18,6 +24,7 @@
|
|||
--color-success: var(--color-green-600);
|
||||
--color-warning: var(--color-yellow-600);
|
||||
--color-destructive: var(--color-red-600);
|
||||
--color-shadow: --alpha(var(--color-black) / 6%);
|
||||
|
||||
/* Gray scale */
|
||||
--color-gray-25: #FAFAFA;
|
||||
|
@ -231,262 +238,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Custom shadow borders used for surfaces / containers */
|
||||
@utility shadow-border-xs {
|
||||
box-shadow: var(--shadow-xs), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-sm {
|
||||
box-shadow: var(--shadow-sm), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-md {
|
||||
box-shadow: var(--shadow-md), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-lg {
|
||||
box-shadow: var(--shadow-lg), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-xl {
|
||||
box-shadow: var(--shadow-xl), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
/* Design system color utilities */
|
||||
@utility text-primary {
|
||||
@apply text-gray-900;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-secondary {
|
||||
@apply text-gray-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-subdued {
|
||||
@apply text-gray-400;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-600;
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-link {
|
||||
@apply text-blue-600;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-blue-500;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-surface {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-black;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-surface-hover {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-surface-inset {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-surface-inset-hover {
|
||||
@apply bg-gray-200;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container {
|
||||
@apply bg-white;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container-hover {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container-inset {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container-inset-hover {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-inverse {
|
||||
@apply bg-gray-800;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-inverse-hover {
|
||||
@apply bg-gray-700;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-overlay {
|
||||
background-color: rgba(var(--color-gray-100), 0.5);
|
||||
|
||||
@variant theme-dark {
|
||||
background-color: var(--color-alpha-black-900);
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-primary {
|
||||
@apply border-alpha-black-300;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-secondary {
|
||||
@apply border-alpha-black-200;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-300;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-tertiary {
|
||||
@apply border-alpha-black-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-200;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-subdued {
|
||||
@apply border-alpha-black-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-100;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-solid {
|
||||
@apply border-black;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-destructive {
|
||||
@apply border-red-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-red-400;
|
||||
}
|
||||
}
|
||||
|
||||
/* Foreground Colors */
|
||||
@utility fg-gray {
|
||||
@apply text-gray-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-contrast {
|
||||
@apply text-gray-400;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-inverse {
|
||||
@apply text-white;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-primary {
|
||||
@apply text-gray-900;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-primary-variant {
|
||||
@apply text-gray-800;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-50;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-secondary {
|
||||
@apply text-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-secondary-variant {
|
||||
@apply text-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-600;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-subdued {
|
||||
@apply text-gray-400;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
/* Specific override for strong tags in prose under dark mode */
|
||||
.prose:where([data-theme=dark], [data-theme=dark] *) strong {
|
||||
color: theme(colors.white) !important;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
[data-theme="dark"] {
|
||||
--color-success: var(--color-green-500);
|
||||
--color-warning: var(--color-yellow-400);
|
||||
--color-destructive: var(--color-red-400);
|
||||
--color-shadow: --alpha(#000000 / 8%);
|
||||
}
|
||||
|
||||
button {
|
||||
@apply cursor-pointer focus-visible:outline-gray-900;
|
||||
}
|
||||
|
@ -733,120 +497,5 @@
|
|||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
|
||||
/* Specific override for strong tags in prose under dark mode */
|
||||
.prose:where([data-theme=dark], [data-theme=dark] *) strong {
|
||||
color: theme(colors.white) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Button Backgrounds */
|
||||
@utility button-bg-primary {
|
||||
@apply bg-gray-900;
|
||||
/* Maps to fg-primary light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-white;
|
||||
/* Maps to fg-primary dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-primary-hover {
|
||||
@apply bg-gray-800;
|
||||
/* Maps to fg-primary-variant light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-50;
|
||||
/* Maps to fg-primary-variant dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-secondary {
|
||||
@apply bg-gray-50; /* Maps to fg-secondary light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700; /* Maps to fg-secondary dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-secondary-hover {
|
||||
@apply bg-gray-100; /* Maps to fg-secondary-variant light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-600; /* Maps to fg-secondary-variant dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-disabled {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-destructive {
|
||||
@apply bg-red-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-red-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-destructive-hover {
|
||||
@apply bg-red-600;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-red-500;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-ghost-hover {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800 fg-inverse;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-outline-hover {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tab Styles */
|
||||
@utility tab-item-active {
|
||||
@apply bg-white;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility tab-item-hover {
|
||||
@apply bg-gray-200;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility tab-bg-group {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-alpha-black-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-nav-indicator {
|
||||
@apply bg-black;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-white;
|
||||
}
|
||||
}
|
||||
|
|
87
app/assets/tailwind/maybe-design-system/background-utils.css
Normal file
87
app/assets/tailwind/maybe-design-system/background-utils.css
Normal file
|
@ -0,0 +1,87 @@
|
|||
@utility bg-surface {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-black;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-surface-hover {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-surface-inset {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-surface-inset-hover {
|
||||
@apply bg-gray-200;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container {
|
||||
@apply bg-white;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container-hover {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container-inset {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-container-inset-hover {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-inverse {
|
||||
@apply bg-gray-800;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-inverse-hover {
|
||||
@apply bg-gray-700;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-overlay {
|
||||
background-color: --alpha(var(--color-gray-100) / 50%);
|
||||
|
||||
@variant theme-dark {
|
||||
background-color: var(--color-alpha-black-900);
|
||||
}
|
||||
}
|
68
app/assets/tailwind/maybe-design-system/border-utils.css
Normal file
68
app/assets/tailwind/maybe-design-system/border-utils.css
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* Custom shadow borders used for surfaces / containers */
|
||||
@utility shadow-border-xs {
|
||||
box-shadow: var(--shadow-xs), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-sm {
|
||||
box-shadow: var(--shadow-sm), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-md {
|
||||
box-shadow: var(--shadow-md), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-lg {
|
||||
box-shadow: var(--shadow-lg), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility shadow-border-xl {
|
||||
box-shadow: var(--shadow-xl), 0px 0px 0px 1px var(--color-alpha-black-50);
|
||||
}
|
||||
|
||||
@utility border-primary {
|
||||
@apply border-alpha-black-300;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-secondary {
|
||||
@apply border-alpha-black-200;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-300;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-tertiary {
|
||||
@apply border-alpha-black-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-200;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-subdued {
|
||||
@apply border-alpha-black-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-alpha-white-100;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-solid {
|
||||
@apply border-black;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility border-destructive {
|
||||
@apply border-red-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply border-red-400;
|
||||
}
|
||||
}
|
109
app/assets/tailwind/maybe-design-system/component-utils.css
Normal file
109
app/assets/tailwind/maybe-design-system/component-utils.css
Normal file
|
@ -0,0 +1,109 @@
|
|||
/* Button Backgrounds */
|
||||
@utility button-bg-primary {
|
||||
@apply bg-gray-900;
|
||||
/* Maps to fg-primary light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-white;
|
||||
/* Maps to fg-primary dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-primary-hover {
|
||||
@apply bg-gray-800;
|
||||
/* Maps to fg-primary-variant light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-50;
|
||||
/* Maps to fg-primary-variant dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-secondary {
|
||||
@apply bg-gray-50; /* Maps to fg-secondary light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700; /* Maps to fg-secondary dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-secondary-hover {
|
||||
@apply bg-gray-100; /* Maps to fg-secondary-variant light */
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-600; /* Maps to fg-secondary-variant dark */
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-disabled {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-destructive {
|
||||
@apply bg-red-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-red-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-destructive-hover {
|
||||
@apply bg-red-600;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-red-500;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-ghost-hover {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800 fg-inverse;
|
||||
}
|
||||
}
|
||||
|
||||
@utility button-bg-outline-hover {
|
||||
@apply bg-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tab Styles */
|
||||
@utility tab-item-active {
|
||||
@apply bg-white;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility tab-item-hover {
|
||||
@apply bg-gray-200;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
@utility tab-bg-group {
|
||||
@apply bg-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-alpha-black-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility bg-nav-indicator {
|
||||
@apply bg-black;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply bg-white;
|
||||
}
|
||||
}
|
63
app/assets/tailwind/maybe-design-system/foreground-utils.css
Normal file
63
app/assets/tailwind/maybe-design-system/foreground-utils.css
Normal file
|
@ -0,0 +1,63 @@
|
|||
@utility fg-gray {
|
||||
@apply text-gray-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-contrast {
|
||||
@apply text-gray-400;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-inverse {
|
||||
@apply text-white;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-primary {
|
||||
@apply text-gray-900;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-primary-variant {
|
||||
@apply text-gray-800;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-50;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-secondary {
|
||||
@apply text-gray-50;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-secondary-variant {
|
||||
@apply text-gray-100;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-600;
|
||||
}
|
||||
}
|
||||
|
||||
@utility fg-subdued {
|
||||
@apply text-gray-400;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
}
|
31
app/assets/tailwind/maybe-design-system/text-utils.css
Normal file
31
app/assets/tailwind/maybe-design-system/text-utils.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
@utility text-primary {
|
||||
@apply text-gray-900;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-secondary {
|
||||
@apply text-gray-500;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-subdued {
|
||||
@apply text-gray-400;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-gray-600;
|
||||
}
|
||||
}
|
||||
|
||||
@utility text-link {
|
||||
@apply text-blue-600;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply text-blue-500;
|
||||
}
|
||||
}
|
15
app/components/button_component.html.erb
Normal file
15
app/components/button_component.html.erb
Normal file
|
@ -0,0 +1,15 @@
|
|||
<%= wrapper_tag do %>
|
||||
<% if icon_only? %>
|
||||
<%= lucide_icon(@icon, class: icon_classes) %>
|
||||
<% else %>
|
||||
<% if @leading_icon %>
|
||||
<%= lucide_icon(@leading_icon, class: icon_classes) %>
|
||||
<% end %>
|
||||
|
||||
<span class="<%= text_classes %>"><%= @text %></span>
|
||||
|
||||
<% if @trailing_icon %>
|
||||
<%= lucide_icon(@trailing_icon, class: icon_classes) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
117
app/components/button_component.rb
Normal file
117
app/components/button_component.rb
Normal file
|
@ -0,0 +1,117 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ButtonComponent < ViewComponent::Base
|
||||
VARIANTS = {
|
||||
primary: {
|
||||
bg: "bg-gray-900 theme-dark:bg-white hover:bg-gray-800 theme-dark:hover:bg-gray-50 disabled:bg-gray-500 theme-dark:disabled:bg-gray-400",
|
||||
fg: "text-white theme-dark:text-gray-900"
|
||||
},
|
||||
secondary: {
|
||||
bg: "bg-gray-50 theme-dark:bg-gray-700 hover:bg-gray-100 theme-dark:hover:bg-gray-600 disabled:bg-gray-200 theme-dark:disabled:bg-gray-600",
|
||||
fg: "text-gray-900 theme-dark:text-white"
|
||||
},
|
||||
outline: {
|
||||
bg: "bg-transparent hover:bg-gray-100 theme-dark:hover:bg-gray-700",
|
||||
fg: "text-gray-900 theme-dark:text-white",
|
||||
border: "border border-gray-900 theme-dark:border-white"
|
||||
},
|
||||
outline_destructive: {
|
||||
bg: "bg-transparent hover:bg-red-100 theme-dark:hover:bg-red-700",
|
||||
fg: "text-destructive",
|
||||
border: "border border-red-500"
|
||||
},
|
||||
ghost: {
|
||||
bg: "bg-transparent hover:bg-gray-100 theme-dark:hover:bg-gray-700",
|
||||
fg: "text-gray-900 theme-dark:text-white"
|
||||
},
|
||||
link_color: {
|
||||
bg: "bg-transparent hover:bg-gray-100 theme-dark:hover:bg-gray-700",
|
||||
fg: "text-gray-900 theme-dark:text-white"
|
||||
},
|
||||
link_gray: {
|
||||
bg: "bg-transparent hover:bg-gray-100 theme-dark:hover:bg-gray-700",
|
||||
fg: "text-gray-900 theme-dark:text-white"
|
||||
},
|
||||
icon: {
|
||||
bg: "bg-transparent hover:bg-gray-100 theme-dark:hover:bg-gray-700 rounded-lg",
|
||||
fg: "fg-gray"
|
||||
}
|
||||
}.freeze
|
||||
|
||||
SIZES = {
|
||||
sm: {
|
||||
icon_container: "w-8 h-8",
|
||||
container: "px-2 py-1 rounded-md",
|
||||
text: "text-sm",
|
||||
icon: "w-4 h-4"
|
||||
},
|
||||
md: {
|
||||
icon_container: "w-10 h-10",
|
||||
container: "px-3 py-2 rounded-lg",
|
||||
text: "text-sm",
|
||||
icon: "w-5 h-5"
|
||||
},
|
||||
lg: {
|
||||
icon_container: "w-12 h-12",
|
||||
container: "px-4 py-3 rounded-xl",
|
||||
text: "text-base",
|
||||
icon: "w-6 h-6"
|
||||
}
|
||||
}
|
||||
|
||||
def initialize(text:, variant: "primary", size: "md", href: nil, leading_icon: nil, trailing_icon: nil, icon: nil, **options)
|
||||
@text = text
|
||||
@variant = variant.underscore.to_sym
|
||||
@size = size.to_sym
|
||||
@href = href
|
||||
@leading_icon = leading_icon
|
||||
@trailing_icon = trailing_icon
|
||||
@icon = icon
|
||||
@options = options
|
||||
end
|
||||
|
||||
def wrapper_tag(&block)
|
||||
html_tag = @href ? "a" : "button"
|
||||
|
||||
if @href.present?
|
||||
content_tag(html_tag, class: container_classes, href: @href, **@options, &block)
|
||||
else
|
||||
content_tag(html_tag, class: container_classes, **@options, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def text_classes
|
||||
[
|
||||
size_meta[:text],
|
||||
variant_meta[:fg]
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
def icon_classes
|
||||
[
|
||||
size_meta[:icon],
|
||||
variant_meta[:fg]
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
def icon_only?
|
||||
@variant == :icon
|
||||
end
|
||||
|
||||
private
|
||||
def container_classes
|
||||
[
|
||||
"inline-flex items-center justify-center gap-1",
|
||||
@variant == :icon ? size_meta[:icon_container] : size_meta[:container],
|
||||
variant_meta[:bg]
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
def size_meta
|
||||
SIZES[@size]
|
||||
end
|
||||
|
||||
def variant_meta
|
||||
VARIANTS[@variant]
|
||||
end
|
||||
end
|
61
app/components/icon_component.rb
Normal file
61
app/components/icon_component.rb
Normal file
|
@ -0,0 +1,61 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class IconComponent < ViewComponent::Base
|
||||
erb_template <<~ERB
|
||||
<%= tag.div class: container_classes do %>
|
||||
<%= lucide_icon(@icon, class: icon_classes) %>
|
||||
<% end %>
|
||||
ERB
|
||||
|
||||
VARIANTS = {
|
||||
default: {
|
||||
icon: "fg-gray",
|
||||
container: "bg-transparent"
|
||||
}
|
||||
}
|
||||
|
||||
SIZES = {
|
||||
sm: {
|
||||
icon: "w-4 h-4",
|
||||
container: "w-8 h-8"
|
||||
},
|
||||
md: {
|
||||
icon: "w-5 h-5",
|
||||
container: "w-10 h-10"
|
||||
},
|
||||
lg: {
|
||||
icon: "w-6 h-6",
|
||||
container: "w-12 h-12"
|
||||
}
|
||||
}
|
||||
|
||||
def initialize(icon, variant: "default", size: "md")
|
||||
@icon = icon
|
||||
@variant = variant.to_sym
|
||||
@size = size.to_sym
|
||||
end
|
||||
|
||||
def icon_classes
|
||||
[
|
||||
size_meta[:icon],
|
||||
variant_meta[:icon]
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
def container_classes
|
||||
[
|
||||
"flex justify-center items-center",
|
||||
size_meta[:container],
|
||||
variant_meta[:container]
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
private
|
||||
def variant_meta
|
||||
VARIANTS[@variant]
|
||||
end
|
||||
|
||||
def size_meta
|
||||
SIZES[@size]
|
||||
end
|
||||
end
|
3
app/controllers/lookbooks_controller.rb
Normal file
3
app/controllers/lookbooks_controller.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
class LookbooksController < Lookbook::PreviewController
|
||||
layout "lookbooks"
|
||||
end
|
14
app/views/layouts/lookbooks.html.erb
Normal file
14
app/views/layouts/lookbooks.html.erb
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html data-theme="<%= params.dig(:lookbook, :display, :theme) %>">
|
||||
<head>
|
||||
<title>Component Preview</title>
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
|
||||
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
|
||||
<%= javascript_importmap_tags %>
|
||||
</head>
|
||||
<body class="p-4 bg-surface">
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
|
@ -8,9 +8,9 @@
|
|||
<%= link_to new_account_path(step: "method_select", classification: "asset"),
|
||||
class: "btn btn--primary flex items-center justify-center gap-2 rounded-full w-9 h-9 md:hidden",
|
||||
data: { turbo_frame: "modal" } do %>
|
||||
<span class="flex items-center justify-center">
|
||||
<%= lucide_icon("plus", class: "size-5") %>
|
||||
</span>
|
||||
<span class="flex items-center justify-center">
|
||||
<%= lucide_icon("plus", class: "size-5") %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -33,5 +33,10 @@ module Maybe
|
|||
if Rails.application.credentials.active_record_encryption.present?
|
||||
config.active_record.encryption = Rails.application.credentials.active_record_encryption
|
||||
end
|
||||
|
||||
config.view_component.preview_controller = "LookbooksController"
|
||||
config.lookbook.preview_display_options = {
|
||||
theme: [ "light", "dark" ] # available in view as params[:theme]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
3
config/initializers/mini_profiler.rb
Normal file
3
config/initializers/mini_profiler.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
Rails.application.configure do
|
||||
Rack::MiniProfiler.config.skip_paths = [ "/design-system" ]
|
||||
end
|
|
@ -8,6 +8,8 @@ Rails.application.routes.draw do
|
|||
delete :disable
|
||||
end
|
||||
|
||||
mount Lookbook::Engine, at: "/design-system"
|
||||
|
||||
# Uses basic auth - see config/initializers/sidekiq.rb
|
||||
mount Sidekiq::Web => "/sidekiq"
|
||||
|
||||
|
|
19
test/components/previews/button_component_preview.rb
Normal file
19
test/components/previews/button_component_preview.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
class ButtonComponentPreview < ViewComponent::Preview
|
||||
# @param variant select {{ ButtonComponent::VARIANTS.keys }}
|
||||
# @param size select {{ ButtonComponent::SIZES.keys }}
|
||||
# @param disabled toggle
|
||||
# @param leading_icon text
|
||||
# @param trailing_icon text
|
||||
# @param icon text "This is only used for icon-only buttons"
|
||||
def default(variant: "primary", size: "md", disabled: false, leading_icon: "plus", trailing_icon: nil, icon: "circle")
|
||||
render ButtonComponent.new(
|
||||
text: "Sample button",
|
||||
variant: variant,
|
||||
size: size,
|
||||
disabled: disabled,
|
||||
leading_icon: leading_icon,
|
||||
trailing_icon: trailing_icon,
|
||||
icon: icon
|
||||
)
|
||||
end
|
||||
end
|
11
test/components/previews/icon_component_preview.rb
Normal file
11
test/components/previews/icon_component_preview.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class IconComponentPreview < ViewComponent::Preview
|
||||
# @param variant select {{ IconComponent::VARIANTS.keys }}
|
||||
# @param size select {{ IconComponent::SIZES.keys }}
|
||||
def default(variant: "default", size: "md")
|
||||
render IconComponent.new(
|
||||
"circle-user",
|
||||
variant: variant,
|
||||
size: size
|
||||
)
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue