diff --git a/assets/css/app.css b/assets/css/app.css index bb4fd9164..194747ce5 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -1,32 +1,157 @@ -@import "tailwindcss/base"; -@import "./components.css"; -@import "tailwindcss/components"; -@import "tailwindcss/utilities"; +@import 'tailwindcss'; +@import './components.css'; + +@plugin "@tailwindcss/forms"; +@plugin "../vendor/heroicons.js"; +@plugin "../vendor/fontawesome.js"; + +@source "../js"; +@source "../../lib/safira_web.ex"; +@source "../../lib/safira_web"; + +@font-face { + font-family: 'Terminal'; + src: url('/fonts/Terminal/TerminalGrotesque.ttf') format('truetype'); + font-display: swap; +} + +@font-face { + font-family: 'Inter-Regular'; + src: url('/fonts/Inter/Inter-Regular.ttf') format('truetype'); + font-display: swap; +} + +@theme { + --color-primary: #04041c; + --color-accent: #ffdb0d; + --color-light: #ffffff; + --color-light-muted: #a1a1aa; + --color-light-shade: #e5e7eb; + --color-dark: #09090b; + --color-dark-muted: #71717a; + --color-dark-shade: #27272a; + + --color-lightMuted: #a1a1aa; + --color-lightShade: #e5e7eb; + --color-darkMuted: #71717a; + --color-darkShade: #27272a; + + --color-secondary-50: var(--color-sky-50); + --color-secondary-100: var(--color-sky-100); + --color-secondary-200: var(--color-sky-200); + --color-secondary-300: var(--color-sky-300); + --color-secondary-400: var(--color-sky-400); + --color-secondary-500: var(--color-sky-500); + --color-secondary-600: var(--color-sky-600); + --color-secondary-700: var(--color-sky-700); + --color-secondary-800: var(--color-sky-800); + --color-secondary-900: var(--color-sky-900); + --color-secondary-950: var(--color-sky-950); + + --color-success-50: var(--color-green-50); + --color-success-100: var(--color-green-100); + --color-success-200: var(--color-green-200); + --color-success-300: var(--color-green-300); + --color-success-400: var(--color-green-400); + --color-success-500: var(--color-green-500); + --color-success-600: var(--color-green-600); + --color-success-700: var(--color-green-700); + --color-success-800: var(--color-green-800); + --color-success-900: var(--color-green-900); + --color-success-950: var(--color-green-950); + + --color-danger-50: var(--color-red-50); + --color-danger-100: var(--color-red-100); + --color-danger-200: var(--color-red-200); + --color-danger-300: var(--color-red-300); + --color-danger-400: var(--color-red-400); + --color-danger-500: var(--color-red-500); + --color-danger-600: var(--color-red-600); + --color-danger-700: var(--color-red-700); + --color-danger-800: var(--color-red-800); + --color-danger-900: var(--color-red-900); + --color-danger-950: var(--color-red-950); + + --color-warning-50: var(--color-yellow-50); + --color-warning-100: var(--color-yellow-100); + --color-warning-200: var(--color-yellow-200); + --color-warning-300: var(--color-yellow-300); + --color-warning-400: var(--color-yellow-400); + --color-warning-500: var(--color-yellow-500); + --color-warning-600: var(--color-yellow-600); + --color-warning-700: var(--color-yellow-700); + --color-warning-800: var(--color-yellow-800); + --color-warning-900: var(--color-yellow-900); + --color-warning-950: var(--color-yellow-950); + + --color-info-50: var(--color-blue-50); + --color-info-100: var(--color-blue-100); + --color-info-200: var(--color-blue-200); + --color-info-300: var(--color-blue-300); + --color-info-400: var(--color-blue-400); + --color-info-500: var(--color-blue-500); + --color-info-600: var(--color-blue-600); + --color-info-700: var(--color-blue-700); + --color-info-800: var(--color-blue-800); + --color-info-900: var(--color-blue-900); + --color-info-950: var(--color-blue-950); + + --font-terminal: 'Terminal', ui-monospace, monospace; + --font-iregular: 'Inter-Regular', ui-sans-serif, sans-serif; + + --animate-slide-in: slide-in 1.5s ease-in-out; + --animate-fade-in: fade-in 0.5s ease-in-out; + --animate-fade-in-slow: fade-in 1.5s ease-in-out; +} + +@keyframes slide-in { + 0% { + transform: translateY(20%); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes fade-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +/* Add variants based on LiveView classes */ +@custom-variant phx-no-feedback (.phx-no-feedback&, .phx-no-feedback &); +@custom-variant phx-click-loading (.phx-click-loading&, .phx-click-loading &); +@custom-variant phx-submit-loading (.phx-submit-loading&, .phx-submit-loading &); +@custom-variant phx-change-loading (.phx-change-loading&, .phx-change-loading &); + +/* Use the data attribute for dark mode */ +@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); -/* For Webkit-based browsers (Chrome, Safari and Opera) */ .scrollbar-hide::-webkit-scrollbar { - display: none; + display: none; } -/* For IE, Edge and Firefox */ .scrollbar-hide { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; + scrollbar-width: none; } -/* Disable autofill background on inputs */ input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, -input:-webkit-autofill:active { - -webkit-box-shadow: 0 0 0 30px white inset !important; +textarea:-webkit-autofill, +textarea:-webkit-autofill:hover textarea:-webkit-autofill:focus, +select:-webkit-autofill, +select:-webkit-autofill:hover, +select:-webkit-autofill:focus { + -webkit-box-shadow: 0 0 0px 1000px transparent inset; } -@font-face { - font-family: "Terminal"; - src: url("/fonts/Terminal/TerminalGrotesque.ttf") format("truetype"); -} -@font-face { - font-family: "Inter-Regular"; - src: url("/fonts/Inter/Inter-Regular.ttf") format("truetype"); -} \ No newline at end of file +/* Make LiveView wrapper divs transparent for layout */ +[data-phx-session], [data-phx-teleported-src] { display: contents } diff --git a/assets/css/components.css b/assets/css/components.css index abff2e05a..3d213182b 100644 --- a/assets/css/components.css +++ b/assets/css/components.css @@ -1,5 +1,5 @@ -@import "components/avatar.css"; -@import "components/field.css"; -@import "components/dropdown.css"; -@import "components/coinflip.css"; -@import "components/slots_reel.css" \ No newline at end of file +@import './components/avatar.css'; +@import './components/field.css'; +@import './components/dropdown.css'; +@import './components/coinflip.css'; +@import './components/slots_reel.css'; diff --git a/assets/css/components/dropdown.css b/assets/css/components/dropdown.css index 6f0219756..11bd4424f 100644 --- a/assets/css/components/dropdown.css +++ b/assets/css/components/dropdown.css @@ -7,7 +7,7 @@ @apply w-5 h-5 ml-2 -mr-1 dark:text-gray-100; } .safira-dropdown__menu-items-wrapper { - @apply absolute z-30 w-56 mt-2 bg-white rounded-md shadow-lg dark:bg-gray-800 ring-1 ring-black ring-opacity-5 focus:outline-none; + @apply absolute z-30 w-56 mt-2 bg-white rounded-md shadow-lg dark:bg-gray-800 ring-1 ring-black/5 focus:outline-none; } .safira-dropdown__menu-items-wrapper-placement--left { @apply right-0 origin-top-right; @@ -32,4 +32,4 @@ } .safira-dropdown__ellipsis { @apply w-5 h-5; -} \ No newline at end of file +} diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js deleted file mode 100644 index 24eebd0b3..000000000 --- a/assets/tailwind.config.js +++ /dev/null @@ -1,139 +0,0 @@ -const plugin = require("tailwindcss/plugin") -const fs = require("fs") -const path = require("path") -const colors = require("tailwindcss/colors"); - -module.exports = { - darkMode: "selector", - content: [ - "./js/**/*.js", - "../lib/safira_web.ex", - "../lib/safira_web/**/*.*ex" - ], - theme: { - extend: { - colors: { - primary: "#04041C", - accent: "#ffdb0d", - light: "#ffffff", - lightMuted: "#a1a1aa", - lightShade: "#e5e7eb", - dark: "#09090b", - darkMuted: "#71717a", - darkShade: "#27272a", - secondary: colors.sky, - success: colors.green, - danger: colors.red, - warning: colors.yellow, - info: colors.blue, - gray: colors.gray - }, - fontFamily: { - terminal: ["Terminal"], - iregular: ["Inter-Regular"] - }, - animation: { - "slide-in": "slide-in 1.5s ease-in-out", - "fade-in": "fade-in 0.5s ease-in-out", - "fade-in-slow": "fade-in 1.5s ease-in-out" - }, - keyframes: { - "slide-in": { - "0%": { transform: "translateY(20%)", opacity: 0}, - "100%": { transform: "translateY(0)", opacity: 1}, - }, - "fade-in": { - "0%": { opacity: 0}, - "100%": { opacity: 1}, - } - }, - }, - }, - plugins: [ - require("@tailwindcss/forms"), - // Allows prefixing tailwind classes with LiveView classes to add rules - // only when LiveView classes are applied, for example: - // - //
- // - plugin(({addVariant}) => addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])), - plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])), - plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])), - plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])), - - // Embeds Heroicons (https://heroicons.com) into your app.css bundle - // See your `CoreComponents.icon/1` for more information. - // - plugin(function({matchComponents, theme}) { - let iconsDir = path.join(__dirname, "../deps/heroicons/optimized") - let values = {} - let icons = [ - ["", "/24/outline"], - ["-solid", "/24/solid"], - ["-mini", "/20/solid"], - ["-micro", "/16/solid"] - ] - icons.forEach(([suffix, dir]) => { - fs.readdirSync(path.join(iconsDir, dir)).forEach(file => { - let name = path.basename(file, ".svg") + suffix - values[name] = {name, fullPath: path.join(iconsDir, dir, file)} - }) - }) - matchComponents({ - "hero": ({name, fullPath}) => { - let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "") - let size = theme("spacing.6") - if (name.endsWith("-mini")) { - size = theme("spacing.5") - } else if (name.endsWith("-micro")) { - size = theme("spacing.4") - } - return { - [`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`, - "-webkit-mask": `var(--hero-${name})`, - "mask": `var(--hero-${name})`, - "mask-repeat": "no-repeat", - "background-color": "currentColor", - "vertical-align": "middle", - "display": "inline-block", - "width": size, - "height": size - } - } - }, {values}) - }), - - // Embeds FontAwesome icons (https://fontawesome.com/) into app.css bundle - plugin(function ({ matchComponents, theme }) { - let iconsDir = path.join(__dirname, "../deps/fontawesome/svgs") - let values = {} - let icons = [ - ["", "", "/regular"], - ["-solid", "", "/solid"], - ["", "brand-", "/brands"] - ] - icons.forEach(([suffix, prefix, dir]) => { - fs.readdirSync(path.join(iconsDir, dir)).forEach(file => { - let name = prefix + path.basename(file, ".svg") + suffix - values[name] = { name, fullPath: path.join(iconsDir, dir, file) } - }) - }) - matchComponents({ - "fa": ({ name, fullPath }) => { - let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "") - return { - [`--fa-${name}`]: `url('data:image/svg+xml;utf8,${content}')`, - "-webkit-mask": `var(--fa-${name})`, - "mask": `var(--fa-${name})`, - "mask-repeat": "no-repeat", - "background-color": "currentColor", - "vertical-align": "middle", - "display": "inline-block", - "width": theme("spacing.5"), - "height": theme("spacing.5") - } - } - }, { values }) - }) - ] -} \ No newline at end of file diff --git a/assets/vendor/fontawesome.js b/assets/vendor/fontawesome.js new file mode 100644 index 000000000..16700bc7d --- /dev/null +++ b/assets/vendor/fontawesome.js @@ -0,0 +1,37 @@ +const plugin = require("tailwindcss/plugin") +const fs = require('fs') +const path = require('path') + +module.exports = plugin(function({ matchComponents, theme }) { + let iconsDir = path.join(__dirname, "../../deps/fontawesome/svgs") + let values = {} + let icons = [ + ["", "", "/regular"], + ["-solid", "", "/solid"], + ["", "brand-", "/brands"] + ] + + icons.forEach(([suffix, prefix, dir]) => { + fs.readdirSync(path.join(iconsDir, dir)).forEach(file => { + let name = prefix + path.basename(file, ".svg") + suffix + values[name] = { name, fullPath: path.join(iconsDir, dir, file) } + }) + }) + + matchComponents({ + "fa": ({ name, fullPath }) => { + let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "") + return { + [`--fa-${name}`]: `url('data:image/svg+xml;utf8,${content}')`, + "-webkit-mask": `var(--fa-${name})`, + "mask": `var(--fa-${name})`, + "mask-repeat": "no-repeat", + "background-color": "currentColor", + "vertical-align": "middle", + "display": "inline-block", + "width": theme("spacing.5"), + "height": theme("spacing.5") + } + } + }, { values }) +}) \ No newline at end of file diff --git a/assets/vendor/heroicons.js b/assets/vendor/heroicons.js new file mode 100644 index 000000000..f85cf7eae --- /dev/null +++ b/assets/vendor/heroicons.js @@ -0,0 +1,43 @@ +const plugin = require("tailwindcss/plugin") +const fs = require("fs") +const path = require("path") + +module.exports = plugin(function({matchComponents, theme}) { + let iconsDir = path.join(__dirname, "../../deps/heroicons/optimized") + let values = {} + let icons = [ + ["", "/24/outline"], + ["-solid", "/24/solid"], + ["-mini", "/20/solid"], + ["-micro", "/16/solid"] + ] + icons.forEach(([suffix, dir]) => { + fs.readdirSync(path.join(iconsDir, dir)).forEach(file => { + let name = path.basename(file, ".svg") + suffix + values[name] = {name, fullPath: path.join(iconsDir, dir, file)} + }) + }) + matchComponents({ + "hero": ({name, fullPath}) => { + let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "") + content = encodeURIComponent(content) + let size = theme("spacing.6") + if (name.endsWith("-mini")) { + size = theme("spacing.5") + } else if (name.endsWith("-micro")) { + size = theme("spacing.4") + } + return { + [`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`, + "-webkit-mask": `var(--hero-${name})`, + "mask": `var(--hero-${name})`, + "mask-repeat": "no-repeat", + "background-color": "currentColor", + "vertical-align": "middle", + "display": "inline-block", + "width": size, + "height": size + } + } + }, {values}) +}) \ No newline at end of file diff --git a/config/config.exs b/config/config.exs index 390006aac..703513350 100644 --- a/config/config.exs +++ b/config/config.exs @@ -54,14 +54,13 @@ config :esbuild, # Configure tailwind (the version is required) config :tailwind, - version: "3.4.1", + version: "4.1.12", safira: [ args: ~w( - --config=tailwind.config.js - --input=css/app.css - --output=../priv/static/assets/app.css + --input=assets/css/app.css + --output=priv/static/assets/app.css ), - cd: Path.expand("../assets", __DIR__) + cd: Path.expand("..", __DIR__) ] # Configures Elixir's Logger diff --git a/lib/safira_web/components/sidebar.ex b/lib/safira_web/components/sidebar.ex index 017b5a370..d6a1e82fe 100644 --- a/lib/safira_web/components/sidebar.ex +++ b/lib/safira_web/components/sidebar.ex @@ -27,13 +27,13 @@ defmodule SafiraWeb.Components.Sidebar do ~H"""