diff --git a/README.md b/README.md index 21c7f95..7098b95 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,31 @@ Import `base.css` once in your main stylesheet. It includes `@import "tailwindcs /* ...see base.css for the full list of tokens */ } -.twui-button-general { - @apply rounded-full; +/* Element defaults */ +@layer base { + h1 { + @apply text-5xl font-bold; + } +} + +/* Templates for twui components */ +@layer components { + .twui-button-general { + @apply rounded-full; + } + + .twui-section { + @apply py-20; + } } ``` +### Overriding with utility classes + +Put your own rules inside `@layer base` (element selectors like `h1`, `ul`) or `@layer components` (classes like `.twui-*`). Tailwind's utilities come in the later `utilities` layer, so a class passed to a component (`
`) overrides your stylesheet without `!important`. + +CSS outside any `@layer` beats every layered rule regardless of specificity. A bare `.twui-section { @apply py-44; }` would therefore override `className="py-4"` on every `Section`, which is why these rules belong in a layer. + ## Usage Import from the root entry: diff --git a/src/base.css b/src/base.css index 7111e7f..0057e9f 100644 --- a/src/base.css +++ b/src/base.css @@ -86,57 +86,166 @@ @custom-variant dark (&:where(.dark, .dark *)); -body { - @apply bg-background-light dark:bg-background-dark; - @apply text-foreground-light dark:text-foreground-dark; - font-family: Arial, Helvetica, sans-serif; -} +/* + * Cascade layers: Tailwind v4 puts its utilities in `@layer utilities`, and + * any CSS outside a layer beats every layered rule regardless of specificity. + * Everything here that styles our own markup lives in `base` or `components`, + * so utility classes passed to (or written in) a component always win over + * it without `!important`. Consumers should do the same in their own + * stylesheet: wrap `.twui-*` overrides in `@layer components { ... }`. + */ -.tox-tinymce { - @apply w-full !rounded-default !border-slate-300 dark:!border-white/20; -} +@layer base { + body { + @apply bg-background-light dark:bg-background-dark; + @apply text-foreground-light dark:text-foreground-dark; + font-family: Arial, Helvetica, sans-serif; + } -/* .moving-object { - @apply !bg-green-500; -} */ + option { + @apply dark:bg-background-dark; + } -option { - @apply dark:bg-background-dark; -} + ol { + list-style: decimal; + } -.mobile-paper-hidden { - @apply max-md:p-0 max-md:border-none max-md:bg-transparent; -} + ul { + list-style: disc; + } -::-webkit-scrollbar { - @apply w-2; -} + ul, + ol { + margin-left: 25px; + } -::-webkit-scrollbar-track { - @apply bg-gray rounded-full dark:bg-gray; -} + ::-webkit-scrollbar { + @apply w-2; + } -::-webkit-scrollbar-thumb { - @apply bg-foreground-light/40 rounded-full hover:bg-foreground-light/60; - @apply dark:bg-foreground-dark/40 rounded-full hover:bg-foreground-dark/60; -} + ::-webkit-scrollbar-track { + @apply bg-gray rounded-full dark:bg-gray; + } -* { - scrollbar-width: thin; - scrollbar-color: theme("colors.gray.400") theme("colors.gray.100"); -} + ::-webkit-scrollbar-thumb { + @apply bg-foreground-light/40 rounded-full hover:bg-foreground-light/60; + @apply dark:bg-foreground-dark/40 rounded-full hover:bg-foreground-dark/60; + } -@supports (selector(:where(*))) { - :where(*) { + * { scrollbar-width: thin; scrollbar-color: theme("colors.gray.400") theme("colors.gray.100"); } - .dark :where(*) { - scrollbar-color: theme("colors.gray.500") theme("colors.gray.800"); + @supports (selector(:where(*))) { + :where(*) { + scrollbar-width: thin; + scrollbar-color: theme("colors.gray.400") theme("colors.gray.100"); + } + + .dark :where(*) { + scrollbar-color: theme("colors.gray.500") theme("colors.gray.800"); + } } } +@layer components { + .mobile-paper-hidden { + @apply max-md:p-0 max-md:border-none max-md:bg-transparent; + } + + .normal-text { + @apply text-foreground-light dark:text-foreground-dark; + } + + /* Assistant replies in `elements/ai/AIChat`. Scoped, since Tailwind's + preflight strips the default styles of the elements markdown produces */ + .twui-ai-chat-markdown { + @apply flex flex-col gap-3 leading-7 break-words min-w-0; + } + + .twui-ai-chat-markdown > * { + @apply m-0; + } + + .twui-ai-chat-markdown h1 { + @apply text-2xl font-bold mt-2; + } + + .twui-ai-chat-markdown h2 { + @apply text-xl font-bold mt-2; + } + + .twui-ai-chat-markdown h3, + .twui-ai-chat-markdown h4, + .twui-ai-chat-markdown h5, + .twui-ai-chat-markdown h6 { + @apply text-base font-semibold mt-1; + } + + .twui-ai-chat-markdown ul, + .twui-ai-chat-markdown ol { + @apply ml-5 flex flex-col gap-1; + } + + .twui-ai-chat-markdown li > ul, + .twui-ai-chat-markdown li > ol { + @apply mt-1; + } + + .twui-ai-chat-markdown a { + @apply text-link dark:text-link-dark underline; + } + + .twui-ai-chat-markdown blockquote { + @apply border-l-4 border-solid border-slate-300 dark:border-white/20 pl-4 opacity-80; + } + + .twui-ai-chat-markdown hr { + @apply border-slate-200 dark:border-white/10; + } + + .twui-ai-chat-markdown :not(pre) > code { + @apply px-1.5 py-0.5 rounded-default-sm text-[0.875em]; + @apply bg-slate-100 dark:bg-white/10; + } + + .twui-ai-chat-markdown-table { + @apply w-full overflow-x-auto; + } + + .twui-ai-chat-markdown table { + @apply w-full border-collapse text-sm; + } + + .twui-ai-chat-markdown th, + .twui-ai-chat-markdown td { + @apply border border-solid border-slate-200 dark:border-white/10 px-3 py-1.5 text-left; + } + + .twui-ai-chat-markdown th { + @apply font-semibold bg-slate-50 dark:bg-white/5; + } +} + +@layer utilities { + /* Deliberately overrides CodeBlock's own utilities on its `
` (e.g.
+       `whitespace-pre-wrap`) inside chat replies. It sits in the utilities
+       layer so its higher specificity beats those single-class utilities */
+    .twui-ai-chat-markdown .twui-code-block-pre {
+        @apply text-sm text-white whitespace-pre px-3 pt-2 pb-3;
+    }
+}
+
+/*
+ * Overrides for third-party widgets (TinyMCE, Ace). These stay unlayered on
+ * purpose: they have to beat the widgets' own unlayered stylesheets.
+ */
+
+.tox-tinymce {
+    @apply w-full !rounded-default !border-slate-300 dark:!border-white/20;
+}
+
 .ace_editor {
     @apply dark:bg-background-dark;
 }
@@ -173,93 +282,3 @@ option {
 .ace_gutter-active-line {
     @apply dark:!bg-white/5;
 }
-
-.normal-text {
-    @apply text-foreground-light dark:text-foreground-dark;
-}
-
-ol {
-    list-style: decimal;
-}
-
-ul {
-    list-style: disc;
-}
-
-ul,
-ol {
-    margin-left: 25px;
-}
-
-/* Assistant replies in `elements/ai/AIChat`. Scoped, since Tailwind's
-   preflight strips the default styles of the elements markdown produces */
-.twui-ai-chat-markdown {
-    @apply flex flex-col gap-3 leading-7 break-words min-w-0;
-}
-
-.twui-ai-chat-markdown > * {
-    @apply m-0;
-}
-
-.twui-ai-chat-markdown h1 {
-    @apply text-2xl font-bold mt-2;
-}
-
-.twui-ai-chat-markdown h2 {
-    @apply text-xl font-bold mt-2;
-}
-
-.twui-ai-chat-markdown h3,
-.twui-ai-chat-markdown h4,
-.twui-ai-chat-markdown h5,
-.twui-ai-chat-markdown h6 {
-    @apply text-base font-semibold mt-1;
-}
-
-.twui-ai-chat-markdown ul,
-.twui-ai-chat-markdown ol {
-    @apply ml-5 flex flex-col gap-1;
-}
-
-.twui-ai-chat-markdown li > ul,
-.twui-ai-chat-markdown li > ol {
-    @apply mt-1;
-}
-
-.twui-ai-chat-markdown a {
-    @apply text-link dark:text-link-dark underline;
-}
-
-.twui-ai-chat-markdown blockquote {
-    @apply border-l-4 border-solid border-slate-300 dark:border-white/20 pl-4 opacity-80;
-}
-
-.twui-ai-chat-markdown hr {
-    @apply border-slate-200 dark:border-white/10;
-}
-
-.twui-ai-chat-markdown :not(pre) > code {
-    @apply px-1.5 py-0.5 rounded-default-sm text-[0.875em];
-    @apply bg-slate-100 dark:bg-white/10;
-}
-
-.twui-ai-chat-markdown .twui-code-block-pre {
-    @apply text-sm text-white whitespace-pre px-3 pt-2 pb-3;
-}
-
-.twui-ai-chat-markdown-table {
-    @apply w-full overflow-x-auto;
-}
-
-.twui-ai-chat-markdown table {
-    @apply w-full border-collapse text-sm;
-}
-
-.twui-ai-chat-markdown th,
-.twui-ai-chat-markdown td {
-    @apply border border-solid border-slate-200 dark:border-white/10 px-3 py-1.5 text-left;
-}
-
-.twui-ai-chat-markdown th {
-    @apply font-semibold bg-slate-50 dark:bg-white/5;
-}