From 6b028365e4890d9454d1fb24ea87ce7f49eab54a Mon Sep 17 00:00:00 2001 From: Randall Wilk <22842525+rawilk@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:31:38 -0500 Subject: [PATCH] Provide better dark mode support (#93) --- docs/advanced-usage/customizing-css.md | 33 +- docs/upgrade.md | 21 ++ resources/css/dark-mode.css | 329 ------------------ resources/css/filepond.css | 21 -- resources/css/index.css | 1 - resources/css/quill.css | 253 -------------- resources/js/tailwind-plugins/dark-mode.js | 322 +++++++++++++++++ .../tailwind-plugins/util/addDarkVariant.js | 17 + .../tailwind-plugins/util/darkModeSelector.js | 12 + 9 files changed, 403 insertions(+), 606 deletions(-) delete mode 100644 resources/css/dark-mode.css create mode 100644 resources/js/tailwind-plugins/dark-mode.js create mode 100644 resources/js/tailwind-plugins/util/addDarkVariant.js create mode 100644 resources/js/tailwind-plugins/util/darkModeSelector.js diff --git a/docs/advanced-usage/customizing-css.md b/docs/advanced-usage/customizing-css.md index 5f12863..af4adbf 100644 --- a/docs/advanced-usage/customizing-css.md +++ b/docs/advanced-usage/customizing-css.md @@ -97,6 +97,9 @@ module.exports = { // Only necessary if you're going to use the switch-toggle component with different colors require("./vendor/rawilk/laravel-form-components/resources/js/tailwind-plugins/switch-toggle"), + + // Only necessary if you're going to support dark mode + require("./vendor/rawilk/laravel-form-components/resources/js/tailwind-plugins/dark-mode"), ], }; ``` @@ -150,6 +153,14 @@ module.exports = { // For checkbox/radio sizing pattern: /form-choice--*/, }, + + // For dark mode... + { + // quill editor classes + pattern: /ql--*/, + }, + 'filepond--panel-root', + 'filepond--root', ], }; ``` @@ -177,7 +188,25 @@ For a full reference of the variables you can set in your CSS, please refer to t ## Dark Mode -The package's components have also been styled for dark mode and will work with both the class based and OS based strategies. If you are using the class based dark mode -strategy, be sure to use the default `dark` class for dark mode. +The package's components have also been styled for dark mode and will work with both the class based and OS based strategies. A custom dark mode selector is also supported too. + +To opt in to dark mode, you will need to add the `dark-mode` plugin to your Tailwind configuration file. See [Plugins](#user-content-plugins) for more information. By default, all +components are styled for dark mode in this plugin. You may opt out of certain components being styled here if you're not using them. Here is an example of all the options you can +pass to the plugin: + +```js +// tailwind.config.js + +module.exports = { + plugins: [ + // ... + require("./vendor/rawilk/laravel-form-components/resources/js/tailwind-plugins/dark-mode")({ + quill: false, + filepond: false, + }), + ] +}; +``` For more information, please refer to [Tailwind's Dark Mode Documentation](https://tailwindcss.com/docs/dark-mode). + diff --git a/docs/upgrade.md b/docs/upgrade.md index aea7ed3..84264b5 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -3,6 +3,27 @@ title: Upgrade sort: 4 --- +## Upgrading from v8 to v8.1.0 + +Although technically there is a breaking change in this version of v8, I've decided against bumping a major version number since the change does not affect functionality of the package. + +### Dark Mode + +To allow for more flexibility for dark mode configuration in Tailwind, a new `dark-mode` Tailwind plugin has been added, which you will need to add to your project's Tailwind config file. +If your app does not support dark mode, you don't need to worry about this change. + +```js +// tailwind.config.js +module.exports = { + plugins: [ + // ... + require('./vendor/rawilk/laravel-form-components/resources/js/tailwind-plugins/dark-mode'), + ], +}; +``` + +The `dark-mode.css` file has also been removed, so if you were manually pulling that into your stylesheets, you will need to remove the reference to it. + ## Upgrading from v7 to v8 Several breaking changes were introduced in v8. Please read the following carefully before upgrading. This list may not be fully comprehensive, so be sure to check the diff --git a/resources/css/dark-mode.css b/resources/css/dark-mode.css deleted file mode 100644 index 623a0e2..0000000 --- a/resources/css/dark-mode.css +++ /dev/null @@ -1,329 +0,0 @@ -@layer components { - /* class strategy */ - .dark { - /* choice */ - .form-choice { - color: var(--choice-dark-ring-color); - border-color: var(--input-dark-border-color); - background-color: var(--input-dark-background-color); - --tw-ring-offset-color: var(--choice-dark-ring-offset-color); - } - - .choice-label label { - --choice-label-color: var(--choice-dark-label-color); - } - - .form-choice:focus { - --tw-ring-color: var(--choice-dark-ring-color); - } - - .form-choice:is(:checked, :checked:hover) { - border-color: transparent; - background-color: currentColor; - } - - /* input */ - .form-text { - --input-background-color: var(--input-dark-background-color); - --input-border-color: var(--input-dark-border-color); - color: var(--input-dark-color); - } - - .form-text::placeholder { - color: var(--input-dark-placeholder-color); - } - - .form-text:is([disabled], [readonly]) { - --input-disabled-bg-color: var(--input-dark-disabled-bg-color); - --input-disabled-border-color: var(--input-dark-disabled-border-color); - color: var(--input-dark-disabled-color); - opacity: .4; - } - - .custom-select { - --input-background-color: var(--input-dark-background-color); - --input-border-color: var(--input-dark-border-color); - --input-color: var(--input-dark-color); - --custom-select-menu-bg: var(--custom-select-dark-menu-bg); - --custom-select-menu-color: var(--custom-select-dark-menu-color); - --custom-select-menu-border-color: var(--custom-select-dark-menu-border-color); - --custom-select-opt-group-bg: var(--custom-select-dark-opt-group-bg); - --custom-select-opt-group-border-color: var(--custom-select-dark-opt-group-border-color); - --custom-select-search-border-color: var(--custom-select-dark-search-border-color); - --custom-select-opt-group-color: var(--custom-select-dark-opt-group-color); - --custom-select-option-selected-bg: var(--custom-select-dark-option-selected-bg); - --custom-select-option-selected-color: var(--custom-select-dark-option-selected-color); - --custom-select-option-active-bg: var(--custom-select-dark-option-active-bg); - --custom-select-option-active-color: var(--custom-select-dark-option-active-color); - --custom-select-selected-icon-hover-color: var(--custom-select-dark-selected-icon-hover-color); - --tree-select-child-border-color: var(--tree-select-dark-child-border-color); - --tree-select-child-hover-border-color: var(--tree-select-dark-child-hover-border-color); - } - - .custom-select:is([disabled], [readonly]) { - --input-disabled-bg-color: var(--input-dark-disabled-bg-color); - --input-disabled-border-color: var(--input-dark-disabled-border-color); - } - - /* file upload */ - .file-upload__input { - --input-background-color: var(--input-dark-background-color); - --input-border-color: var(--input-dark-border-color); - --input-color: var(--input-dark-color); - } - - .file-upload__input::file-selector-button { - --file-upload-button-color: var(--file-upload-button-dark-color); - --file-upload-button-bg: var(--file-upload-button-dark-bg); - } - - .file-upload__input:is([disabled], [readonly]) { - background-color: var(--input-dark-disabled-bg-color); - border-color: var(--input-dark-disabled-border-color); - color: var(--input-dark-disabled-color); - opacity: .4; - } - - .file-upload__input:is([disabled], [readonly])::file-selector-button { - @apply opacity-75; - background-color: var(--file-upload-button-dark-disabled-bg); - } - - /* addons */ - :is(.leading-addon, .trailing-addon) { - --leading-addon-background-color: var(--leading-addon-dark-background-color); - --leading-addon-color: var(--leading-addon-dark-color); - border-color: var(--input-dark-border-color); - } - - :is(.inline-addon, .trailing-inline-addon) { - color: var(--input-dark-color); - } - - .inline-addon:has(+ .input-error), - .input-error + .trailing-inline-addon { - color: var(--input-error-dark-color); - } - - .clear-button:hover { - --clear-button-hover-bg: var(--clear-button-dark-hover-bg); - --clear-button-hover-color: var(--clear-button-dark-hover-color); - } - - /* label */ - .form-label { - --label-color: var(--label-dark-color); - } - - /* form group */ - .form-group--border { - --form-group-border-color: var(--form-group-dark-border-color); - } - - /* switch toggle */ - .switch-toggle__label { - --switch-toggle-label-color: var(--switch-toggle-dark-label-color); - } - - .switch-toggle { - background-color: var(--switch-toggle-dark-bg); - border-color: var(--switch-toggle-dark-border-color); - } - - .peer:focus ~ .switch-toggle { - --tw-ring-color: var(--switch-toggle-dark-ring-color); - } - - .peer:disabled ~ .switch-toggle__label { - color: var(--switch-toggle-dark-disabled-label-color); - } - - .peer:focus ~ .switch-toggle--short { - --tw-ring-offset-color: var(--switch-toggle-dark-ring-offset-color); - } - - /* errors */ - .has-error label { - color: var(--input-error-dark-label-color); - } - - .input-error, - .has-error :is(.form-text, .form-select, .custom-select__button, .file-upload__input) { - --input-dark-color: var(--input-error-dark-color); - --input-dark-placeholder-color: var(--input-error-dark-placeholder-color); - --input-dark-border-color: var(--input-error-dark-border-color); - } - - .has-error .file-upload__input::file-selector-button { - --file-upload-button-color: var(--file-upload-button-error-dark-color); - } - - .has-error :is(.ql-container, .ql-toolbar).ql-snow { - border-color: var(--input-error-dark-border-color); - } - } - - /* os strategy */ - @media (prefers-color-scheme: dark) { - /* choice */ - .form-choice { - color: var(--choice-dark-ring-color); - border-color: var(--input-dark-border-color); - background-color: var(--input-dark-background-color); - --tw-ring-offset-color: var(--choice-dark-ring-offset-color); - } - - .choice-label label { - --choice-label-color: var(--choice-dark-label-color); - } - - .form-choice:focus { - --tw-ring-color: var(--choice-dark-ring-color); - } - - .form-choice:is(:checked, :checked:hover) { - border-color: transparent; - background-color: currentColor; - } - - /* input */ - .form-text { - --input-background-color: var(--input-dark-background-color); - --input-border-color: var(--input-dark-border-color); - color: var(--input-dark-color); - } - - .form-text::placeholder { - color: var(--input-dark-placeholder-color); - } - - .form-text:is([disabled], [readonly]) { - --input-disabled-bg-color: var(--input-dark-disabled-bg-color); - --input-disabled-border-color: var(--input-dark-disabled-border-color); - color: var(--input-dark-disabled-color); - opacity: .4; - } - - .custom-select { - --input-background-color: var(--input-dark-background-color); - --input-border-color: var(--input-dark-border-color); - --input-color: var(--input-dark-color); - --custom-select-menu-bg: var(--custom-select-dark-menu-bg); - --custom-select-menu-color: var(--custom-select-dark-menu-color); - --custom-select-menu-border-color: var(--custom-select-dark-menu-border-color); - --custom-select-opt-group-bg: var(--custom-select-dark-opt-group-bg); - --custom-select-opt-group-border-color: var(--custom-select-dark-opt-group-border-color); - --custom-select-search-border-color: var(--custom-select-dark-search-border-color); - --custom-select-opt-group-color: var(--custom-select-dark-opt-group-color); - --custom-select-option-selected-bg: var(--custom-select-dark-option-selected-bg); - --custom-select-option-selected-color: var(--custom-select-dark-option-selected-color); - --custom-select-option-active-bg: var(--custom-select-dark-option-active-bg); - --custom-select-option-active-color: var(--custom-select-dark-option-active-color); - --custom-select-selected-icon-hover-color: var(--custom-select-dark-selected-icon-hover-color); - --tree-select-child-border-color: var(--tree-select-dark-child-border-color); - --tree-select-child-hover-border-color: var(--tree-select-dark-child-hover-border-color); - } - - .custom-select:is([disabled], [readonly]) { - --input-disabled-bg-color: var(--input-dark-disabled-bg-color); - --input-disabled-border-color: var(--input-dark-disabled-border-color); - } - - /* file upload */ - .file-upload__input { - --input-background-color: var(--input-dark-background-color); - --input-border-color: var(--input-dark-border-color); - --input-color: var(--input-dark-color); - } - - .file-upload__input::file-selector-button { - --file-upload-button-color: var(--file-upload-button-dark-color); - --file-upload-button-bg: var(--file-upload-button-dark-bg); - } - - .file-upload__input:is([disabled], [readonly]) { - background-color: var(--input-dark-disabled-bg-color); - border-color: var(--input-dark-disabled-border-color); - color: var(--input-dark-disabled-color); - opacity: .4; - } - - .file-upload__input:is([disabled], [readonly])::file-selector-button { - @apply opacity-75; - background-color: var(--file-upload-button-dark-disabled-bg); - } - - /* addons */ - :is(.leading-addon, .trailing-addon) { - --leading-addon-background-color: var(--leading-addon-dark-background-color); - --leading-addon-color: var(--leading-addon-dark-color); - border-color: var(--input-dark-border-color); - } - - :is(.inline-addon, .trailing-inline-addon) { - color: var(--input-dark-color); - } - - .inline-addon:has(+ .input-error), - .input-error + .trailing-inline-addon { - color: var(--input-error-dark-color); - } - - .clear-button:hover { - --clear-button-hover-bg: var(--clear-button-dark-hover-bg); - --clear-button-hover-color: var(--clear-button-dark-hover-color); - } - - /* label */ - .form-label { - --label-color: var(--label-dark-color); - } - - /* form group */ - .form-group--border { - --form-group-border-color: var(--form-group-dark-border-color); - } - - /* switch toggle */ - .switch-toggle__label { - --switch-toggle-label-color: var(--switch-toggle-dark-label-color); - } - - .switch-toggle { - background-color: var(--switch-toggle-dark-bg); - border-color: var(--switch-toggle-dark-border-color); - } - - .peer:focus ~ .switch-toggle { - --tw-ring-color: var(--switch-toggle-dark-ring-color); - } - - .peer:disabled ~ .switch-toggle__label { - color: var(--switch-toggle-dark-disabled-label-color); - } - - .peer:focus ~ .switch-toggle--short { - --tw-ring-offset-color: var(--switch-toggle-dark-ring-offset-color); - } - - /* errors */ - .has-error label { - color: var(--input-error-dark-label-color); - } - - .input-error, - .has-error :is(.form-text, .form-select, .custom-select__button, .file-upload__input) { - --input-dark-color: var(--input-error-dark-color); - --input-dark-placeholder-color: var(--input-error-dark-placeholder-color); - --input-dark-border-color: var(--input-error-dark-border-color); - } - - .has-error .file-upload__input::file-selector-button { - --file-upload-button-color: var(--file-upload-button-error-dark-color); - } - - .has-error :is(.ql-container, .ql-toolbar).ql-snow { - border-color: var(--input-error-dark-border-color); - } - } -} diff --git a/resources/css/filepond.css b/resources/css/filepond.css index 7dc0726..c213b87 100644 --- a/resources/css/filepond.css +++ b/resources/css/filepond.css @@ -44,24 +44,3 @@ :is(.has-error, .input-error) .filepond--root:hover .filepond--panel-root { background-color: var(--input-error-bg-color); } - -/* dark mode */ -.dark .filepond--panel-root { - border-color: var(--input-dark-border-color); - background-color: var(--input-dark-background-color); -} - -.dark .filepond--root:hover .filepond--panel-root { - background-color: var(--filepond-dark-hover-bg); -} - -@media (prefers-color-scheme: dark) { - .filepond--panel-root { - border-color: var(--input-dark-border-color); - background-color: var(--input-dark-background-color); - } - - .filepond--root:hover .filepond--panel-root { - background-color: var(--filepond-dark-hover-bg); - } -} diff --git a/resources/css/index.css b/resources/css/index.css index bc46347..44b028b 100644 --- a/resources/css/index.css +++ b/resources/css/index.css @@ -14,4 +14,3 @@ @import 'tree-select.css'; @import 'addons.css'; @import 'errors.css'; -@import 'dark-mode.css'; diff --git a/resources/css/quill.css b/resources/css/quill.css index 967cf88..4c9d1bb 100644 --- a/resources/css/quill.css +++ b/resources/css/quill.css @@ -4,256 +4,3 @@ .has-error :is(.ql-container, .ql-toolbar).ql-snow { border-color: var(--input-error-border-color); } - -/* dark mode */ -.dark { - :is(.ql-container, .ql-toolbar).ql-snow { - border-color: var(--input-dark-border-color); - } - - .ql-editor { - background-color: var(--input-dark-background-color); - color: var(--input-dark-color); - } - - .ql-toolbar.ql-snow { - background-color: var(--quill-dark-toolbar-bg); - } - - .ql-toolbar.ql-snow .ql-picker-label { - color: var(--quill-dark-toolbar-color); - } - - .ql-snow .ql-stroke { - stroke: var(--quill-dark-toolbar-color); - } - - .ql-snow .ql-fill, - .ql-snow .ql-stroke.ql-fill { - fill: var(--quill-dark-toolbar-color); - } - - .ql-snow.ql-toolbar button:is(:focus, :hover) { - color: var(--quill-dark-toolbar-hover-color); - } - - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter, - .ql-snow .ql-toolbar button.ql-active .ql-stroke, - .ql-snow .ql-toolbar button.ql-active .ql-stroke-miter, - .ql-snow .ql-toolbar button:focus .ql-stroke, - .ql-snow .ql-toolbar button:focus .ql-stroke-miter, - .ql-snow .ql-toolbar button:hover .ql-stroke, - .ql-snow .ql-toolbar button:hover .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter, - .ql-snow.ql-toolbar button.ql-active .ql-stroke, - .ql-snow.ql-toolbar button.ql-active .ql-stroke-miter, - .ql-snow.ql-toolbar button:focus .ql-stroke, - .ql-snow.ql-toolbar button:focus .ql-stroke-miter, - .ql-snow.ql-toolbar button:hover .ql-stroke, - .ql-snow.ql-toolbar button:hover .ql-stroke-miter { - stroke: var(--quill-dark-toolbar-hover-color); - } - - .ql-snow.ql-toolbar button:is(:focus, :hover) .ql-fill, - .ql-snow.ql-toolbar button:is(:focus, :hover) .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill, - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, - .ql-snow .ql-toolbar button.ql-active .ql-fill, - .ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill, - .ql-snow .ql-toolbar button:focus .ql-fill, - .ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill, - .ql-snow .ql-toolbar button:hover .ql-fill, - .ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, - .ql-snow.ql-toolbar button.ql-active .ql-fill, - .ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill, - .ql-snow.ql-toolbar button:focus .ql-fill, - .ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill, - .ql-snow.ql-toolbar button:hover .ql-fill, - .ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill { - fill: var(--quill-dark-toolbar-hover-color); - } - - .ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options { - background-color: var(--quill-dark-toolbar-dropdown-bg); - border-color: var(--quill-dark-toolbar-dropdown-border-color); - } - - .ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label { - border-color: var(--quill-dark-button-focus-border-color); - } - - .ql-snow .ql-picker-options .ql-picker-item { - color: var(--quill-dark-toolbar-color); - } - - .ql-snow .ql-toolbar .ql-picker-item.ql-selected, - .ql-snow .ql-toolbar .ql-picker-item:hover, - .ql-snow .ql-toolbar .ql-picker-label.ql-active, - .ql-snow .ql-toolbar .ql-picker-label:hover, - .ql-snow .ql-toolbar button.ql-active, - .ql-snow .ql-toolbar button:focus, - .ql-snow .ql-toolbar button:hover, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected, - .ql-snow.ql-toolbar .ql-picker-item:hover, - .ql-snow.ql-toolbar .ql-picker-label.ql-active, - .ql-snow.ql-toolbar .ql-picker-label:hover, - .ql-snow.ql-toolbar button.ql-active, - .ql-snow.ql-toolbar button:focus, - .ql-snow.ql-toolbar button:hover { - color: var(--quill-dark-toolbar-selected-color); - } -} - -@media (prefers-color-scheme: dark) { - :is(.ql-container, .ql-toolbar).ql-snow { - border-color: var(--input-dark-border-color); - } - - .ql-editor { - background-color: var(--input-dark-background-color); - color: var(--input-dark-color); - } - - .ql-toolbar.ql-snow { - background-color: var(--quill-dark-toolbar-bg); - } - - .ql-toolbar.ql-snow .ql-picker-label { - color: var(--quill-dark-toolbar-color); - } - - .ql-snow .ql-stroke { - stroke: var(--quill-dark-toolbar-color); - } - - .ql-snow .ql-fill, - .ql-snow .ql-stroke.ql-fill { - fill: var(--quill-dark-toolbar-color); - } - - .ql-snow.ql-toolbar button:is(:focus, :hover) { - color: var(--quill-dark-toolbar-hover-color); - } - - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter, - .ql-snow .ql-toolbar button.ql-active .ql-stroke, - .ql-snow .ql-toolbar button.ql-active .ql-stroke-miter, - .ql-snow .ql-toolbar button:focus .ql-stroke, - .ql-snow .ql-toolbar button:focus .ql-stroke-miter, - .ql-snow .ql-toolbar button:hover .ql-stroke, - .ql-snow .ql-toolbar button:hover .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter, - .ql-snow.ql-toolbar button.ql-active .ql-stroke, - .ql-snow.ql-toolbar button.ql-active .ql-stroke-miter, - .ql-snow.ql-toolbar button:focus .ql-stroke, - .ql-snow.ql-toolbar button:focus .ql-stroke-miter, - .ql-snow.ql-toolbar button:hover .ql-stroke, - .ql-snow.ql-toolbar button:hover .ql-stroke-miter { - stroke: var(--quill-dark-toolbar-hover-color); - } - - .ql-snow.ql-toolbar button:is(:focus, :hover) .ql-fill, - .ql-snow.ql-toolbar button:is(:focus, :hover) .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill, - .ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill, - .ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill, - .ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill, - .ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, - .ql-snow .ql-toolbar button.ql-active .ql-fill, - .ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill, - .ql-snow .ql-toolbar button:focus .ql-fill, - .ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill, - .ql-snow .ql-toolbar button:hover .ql-fill, - .ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill, - .ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill, - .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill, - .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, - .ql-snow.ql-toolbar button.ql-active .ql-fill, - .ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill, - .ql-snow.ql-toolbar button:focus .ql-fill, - .ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill, - .ql-snow.ql-toolbar button:hover .ql-fill, - .ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill { - fill: var(--quill-dark-toolbar-hover-color); - } - - .ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options { - background-color: var(--quill-dark-toolbar-dropdown-bg); - border-color: var(--quill-dark-toolbar-dropdown-border-color); - } - - .ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label { - border-color: var(--quill-dark-button-focus-border-color); - } - - .ql-snow .ql-picker-options .ql-picker-item { - color: var(--quill-dark-toolbar-color); - } - - .ql-snow .ql-toolbar .ql-picker-item.ql-selected, - .ql-snow .ql-toolbar .ql-picker-item:hover, - .ql-snow .ql-toolbar .ql-picker-label.ql-active, - .ql-snow .ql-toolbar .ql-picker-label:hover, - .ql-snow .ql-toolbar button.ql-active, - .ql-snow .ql-toolbar button:focus, - .ql-snow .ql-toolbar button:hover, - .ql-snow.ql-toolbar .ql-picker-item.ql-selected, - .ql-snow.ql-toolbar .ql-picker-item:hover, - .ql-snow.ql-toolbar .ql-picker-label.ql-active, - .ql-snow.ql-toolbar .ql-picker-label:hover, - .ql-snow.ql-toolbar button.ql-active, - .ql-snow.ql-toolbar button:focus, - .ql-snow.ql-toolbar button:hover { - color: var(--quill-dark-toolbar-selected-color); - } -} diff --git a/resources/js/tailwind-plugins/dark-mode.js b/resources/js/tailwind-plugins/dark-mode.js new file mode 100644 index 0000000..0e7456f --- /dev/null +++ b/resources/js/tailwind-plugins/dark-mode.js @@ -0,0 +1,322 @@ +const darkModeSelector = require('./util/darkModeSelector'); +const addDarkVariant = require('./util/addDarkVariant'); +const plugin = require('tailwindcss/plugin'); + +module.exports = plugin.withOptions(function (options = {}) { + return function ({ addUtilities, config }) { + const darkSelector = darkModeSelector(config('darkMode', 'class')); + const styles = {}; + + const styleFilepond = options.filepond ?? true; + const styleQuill = options.quill ?? true; + + // choice (checkbox, radio) + addDarkVariant(styles, '.form-choice', darkSelector, { + color: 'var(--choice-dark-ring-color)', + borderColor: 'var(--input-dark-border-color)', + backgroundColor: 'var(--input-dark-background-color)', + '--tw-ring-offset-color': 'var(--choice-dark-ring-offset-color)', + '&:focus': { + '--tw-ring-color': 'var(--choice-dark-ring-color)', + }, + '&:checked, &:checked:hover': { + borderColor: 'transparent', + backgroundColor: 'currentColor', + }, + }); + + addDarkVariant(styles, '.choice-label label', darkSelector, { + '--choice-label-color': 'var(--choice-dark-label-color)', + }); + + // input + addDarkVariant(styles, '.form-text', darkSelector, { + '--input-background-color': 'var(--input-dark-background-color)', + '--input-border-color': 'var(--input-dark-border-color)', + color: 'var(--input-dark-color)', + '&::placeholder': { + color: 'var(--input-dark-placeholder-color)', + }, + '&[disabled], &[readonly]': { + '--input-disabled-bg-color': 'var(--input-dark-disabled-bg-color)', + '--input-disabled-border-color': 'var(--input-dark-disabled-border-color)', + color: 'var(--input-dark-disabled-color)', + opacity: '.4', + }, + }); + + // custom select + addDarkVariant(styles, '.custom-select', darkSelector, { + '--input-background-color': 'var(--input-dark-background-color)', + '--input-border-color': 'var(--input-dark-border-color)', + '--input-color': 'var(--input-dark-color)', + '--custom-select-menu-bg': 'var(--custom-select-dark-menu-bg)', + '--custom-select-menu-color': 'var(--custom-select-dark-menu-color)', + '--custom-select-menu-border-color': 'var(--custom-select-dark-menu-border-color)', + '--custom-select-opt-group-bg': 'var(--custom-select-dark-opt-group-bg)', + '--custom-select-opt-group-border-color': 'var(--custom-select-dark-opt-group-border-color)', + '--custom-select-search-border-color': 'var(--custom-select-dark-search-border-color)', + '--custom-select-opt-group-color': 'var(--custom-select-dark-opt-group-color)', + '--custom-select-option-selected-bg': 'var(--custom-select-dark-option-selected-bg)', + '--custom-select-option-selected-color': 'var(--custom-select-dark-option-selected-color)', + '--custom-select-option-active-bg': 'var(--custom-select-dark-option-active-bg)', + '--custom-select-option-active-color': 'var(--custom-select-dark-option-active-color)', + '--custom-select-selected-icon-hover-color': 'var(--custom-select-dark-selected-icon-hover-color)', + '--tree-select-child-border-color': 'var(--tree-select-dark-child-border-color)', + '--tree-select-child-hover-border-color': 'var(--tree-select-dark-child-hover-border-color)', + + '&[disabled], &[readonly]': { + '--input-disabled-bg-color': 'var(--input-dark-disabled-bg-color)', + '--input-disabled-border-color': 'var(--input-dark-disabled-border-color)', + }, + }); + + // file upload + addDarkVariant(styles, '.file-upload__input', darkSelector, { + '--input-background-color': 'var(--input-dark-background-color)', + '--input-border-color': 'var(--input-dark-border-color)', + '--input-color': 'var(--input-dark-color)', + + '&::file-selector-button': { + '--file-upload-button-color': 'var(--file-upload-button-dark-color)', + '--file-upload-button-bg': 'var(--file-upload-button-dark-bg)', + }, + + '&[disabled], &[readonly]': { + backgroundColor: 'var(--input-dark-disabled-bg-color)', + borderColor: 'var(--input-dark-disabled-border-color)', + color: 'var(--input-dark-disabled-color)', + opacity: '.4', + + '&::file-selector-button': { + '@apply opacity-75': {}, + backgroundColor: 'var(--file-upload-button-dark-disabled-bg)', + }, + }, + }); + + // addons + addDarkVariant(styles, ':is(.leading-addon, .trailing-addon)', darkSelector, { + '--leading-addon-background-color': 'var(--leading-addon-dark-background-color)', + '--leading-addon-color': 'var(--leading-addon-dark-color)', + borderColor: 'var(--input-dark-border-color)', + }); + + addDarkVariant(styles, ':is(.inline-addon, .trailing-inline-addon)', darkSelector, { + color: 'var(--input-dark-color)', + }); + + addDarkVariant(styles, '.inline-addon:has(+ .input-error), .input-error + .trailing-inline-addon', darkSelector, { + color: 'var(--input-error-dark-color)', + }); + + addDarkVariant(styles, '.clear-button:hover', darkSelector, { + '--clear-button-hover-bg': 'var(--clear-button-dark-hover-bg)', + '--clear-button-hover-color': 'var(--clear-button-dark-hover-color)', + }); + + // label + addDarkVariant(styles, '.form-label', darkSelector, { + '--label-color': 'var(--label-dark-color)', + }); + + // form group + addDarkVariant(styles, '.form-group--border', darkSelector, { + '--form-group-border-color': 'var(--form-group-dark-border-color)', + }); + + // switch toggle + addDarkVariant(styles, '.switch-toggle__label', darkSelector, { + '--switch-toggle-label-color': 'var(--switch-toggle-dark-label-color)', + }); + + addDarkVariant(styles, '.switch-toggle', darkSelector, { + backgroundColor: 'var(--switch-toggle-dark-bg)', + borderColor: 'var(--switch-toggle-dark-border-color)', + }); + + addDarkVariant(styles, '.peer:focus ~ .switch-toggle', darkSelector, { + '--tw-ring-color': 'var(--switch-toggle-dark-ring-color)', + }); + + addDarkVariant(styles, '.peer:disabled ~ .switch-toggle__label', darkSelector, { + color: 'var(--switch-toggle-dark-disabled-label-color)', + }); + + addDarkVariant(styles, '.peer:focus ~ .switch-toggle--short', darkSelector, { + '--tw-ring-offset-color': 'var(--switch-toggle-dark-ring-offset-color)', + }); + + // filepond + if (styleFilepond) { + addDarkVariant(styles, '.filepond--panel-root', darkSelector, { + borderColor: 'var(--input-dark-border-color)', + backgroundColor: 'var(--input-dark-background-color)', + }); + + addDarkVariant(styles, '.filepond--root:hover .filepond--panel-root', darkSelector, { + backgroundColor: 'var(--filepond-dark-hover-bg)', + }); + } + + // quill + if (styleQuill) { + addDarkVariant(styles, ':is(.ql-container .ql-toolbar).ql-snow', darkSelector, { + borderColor: 'var(--input-dark-border-color)', + }); + + addDarkVariant(styles, '.ql-editor', darkSelector, { + backgroundColor: 'var(--input-dark-background-color)', + color: 'var(--input-dark-color)', + }); + + addDarkVariant(styles, '.ql-toolbar.ql-snow', darkSelector, { + backgroundColor: 'var(--quill-dark-toolbar-bg)', + + '.ql-picker-label': { + color: 'var(--quill-dark-toolbar-color)', + }, + + '.ql-picker.ql-expanded .ql-picker-options': { + backgroundColor: 'var(--quill-dark-toolbar-dropdown-bg)', + borderColor: 'var(--quill-dark-toolbar-dropdown-border-color)', + }, + + '.ql-picker.ql-expanded .ql-picker-label': { + borderColor: 'var(--quill-dark-button-focus-border-color)', + }, + }); + + addDarkVariant(styles, '.ql-snow', darkSelector, { + '.ql-stroke': { + stroke: 'var(--quill-dark-toolbar-color)', + }, + + '.ql-fill, .ql-stroke.ql-fill': { + fill: 'var(--quill-dark-toolbar-color)', + }, + + '&.ql-toolbar button:is(:focus, :hover)': { + color: 'var(--quill-dark-toolbar-hover-color)', + }, + + '.ql-picker-options .ql-picker-item': { + color: 'var(--quill-dark-toolbar-color)', + }, + }); + + let toolbarSelectors = [ + '.ql-snow .ql-toolbar .ql-picker-item.ql-selected', + '.ql-snow .ql-toolbar .ql-picker-item:hover', + '.ql-snow .ql-toolbar .ql-picker-label.ql-active', + '.ql-snow .ql-toolbar .ql-picker-label:hover', + '.ql-snow .ql-toolbar button.ql-active', + '.ql-snow .ql-toolbar button:focus', + '.ql-snow .ql-toolbar button:hover', + '.ql-snow.ql-toolbar .ql-picker-item.ql-selected', + '.ql-snow.ql-toolbar .ql-picker-item:hover', + '.ql-snow.ql-toolbar .ql-picker-label.ql-active', + '.ql-snow.ql-toolbar .ql-picker-label:hover', + '.ql-snow.ql-toolbar button.ql-active', + '.ql-snow.ql-toolbar button:focus', + '.ql-snow.ql-toolbar button:hover', + ].join(', '); + addDarkVariant(styles, toolbarSelectors, darkSelector, { + color: 'var(--quill-dark-toolbar-selected-color)', + }); + + let fillSelectors = [ + '.ql-snow.ql-toolbar button:is(:focus, :hover) .ql-fill', + '.ql-snow.ql-toolbar button:is(:focus, :hover) .ql-stroke.ql-fill', + '.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill', + '.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill', + '.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill', + '.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill', + '.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill', + '.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill', + '.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill', + '.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill', + '.ql-snow .ql-toolbar button.ql-active .ql-fill', + '.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill', + '.ql-snow .ql-toolbar button:focus .ql-fill', + '.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill', + '.ql-snow .ql-toolbar button:hover .ql-fill', + '.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill', + '.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill', + '.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill', + '.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill', + '.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill', + '.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill', + '.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill', + '.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill', + '.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill', + '.ql-snow.ql-toolbar button.ql-active .ql-fill', + '.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill', + '.ql-snow.ql-toolbar button:focus .ql-fill', + '.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill', + '.ql-snow.ql-toolbar button:hover .ql-fill', + '.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill', + ].join(', '); + addDarkVariant(styles, fillSelectors, darkSelector, { + fill: 'var(--quill-dark-toolbar-hover-color)', + }); + + let strokeSelectors = [ + '.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke', + '.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter', + '.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke', + '.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter', + '.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke', + '.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter', + '.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke', + '.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter', + '.ql-snow .ql-toolbar button.ql-active .ql-stroke', + '.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter', + '.ql-snow .ql-toolbar button:focus .ql-stroke', + '.ql-snow .ql-toolbar button:focus .ql-stroke-miter', + '.ql-snow .ql-toolbar button:hover .ql-stroke', + '.ql-snow .ql-toolbar button:hover .ql-stroke-miter', + '.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke', + '.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter', + '.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke', + '.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter', + '.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke', + '.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter', + '.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke', + '.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter', + '.ql-snow.ql-toolbar button.ql-active .ql-stroke', + '.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter', + '.ql-snow.ql-toolbar button:focus .ql-stroke', + '.ql-snow.ql-toolbar button:focus .ql-stroke-miter', + '.ql-snow.ql-toolbar button:hover .ql-stroke', + '.ql-snow.ql-toolbar button:hover .ql-stroke-miter', + ].join(', '); + addDarkVariant(styles, strokeSelectors, darkSelector, { + stroke: 'var(--quill-dark-toolbar-hover-color)', + }); + } + + // errors + addDarkVariant(styles, '.has-error label', darkSelector, { + color: 'var(--input-error-dark-label-color)', + }); + + addDarkVariant(styles, '.input-error, .has-error :is(.form-text, .form-select, .custom-select__button, .file-upload__input)', darkSelector, { + '--input-dark-color': 'var(--input-error-dark-color)', + '--input-dark-placeholder-color': 'var(--input-error-dark-placeholder-color)', + '--input-dark-border-color': 'var(--input-error-dark-border-color)', + }); + + addDarkVariant(styles, '.has-error .file-upload__input::file-selector-button', darkSelector, { + '--file-upload-button-color': 'var(--file-upload-button-error-dark-color)', + }); + + addDarkVariant(styles, '.has-error :is(.ql-container, .ql-toolbar).ql-snow', darkSelector, { + borderColor: 'var(--input-error-dark-border-color)', + }); + + // We use addUtilities instead of addComponents because we want these styles to come after + // all other styles in the stylesheet, so that they can override any other styles. + addUtilities(styles); + }; +}); diff --git a/resources/js/tailwind-plugins/util/addDarkVariant.js b/resources/js/tailwind-plugins/util/addDarkVariant.js new file mode 100644 index 0000000..94c71bb --- /dev/null +++ b/resources/js/tailwind-plugins/util/addDarkVariant.js @@ -0,0 +1,17 @@ +module.exports = (rootObject, selector, darkModeSelector, styles) => { + if (darkModeSelector === '@media (prefers-color-scheme: dark)') { + if (! rootObject.hasOwnProperty(selector)) { + rootObject[selector] = {}; + } + + rootObject[selector][darkModeSelector] = styles; + + return; + } + + if (! rootObject.hasOwnProperty(darkModeSelector)) { + rootObject[darkModeSelector] = {}; + } + + rootObject[darkModeSelector][selector] = styles; +}; diff --git a/resources/js/tailwind-plugins/util/darkModeSelector.js b/resources/js/tailwind-plugins/util/darkModeSelector.js new file mode 100644 index 0000000..9061b79 --- /dev/null +++ b/resources/js/tailwind-plugins/util/darkModeSelector.js @@ -0,0 +1,12 @@ +module.exports = darkMode => { + // Example: { darkMode: ['class', '.is-dark'] } + if (Array.isArray(darkMode)) { + return darkMode[0] === 'class' + ? darkMode[1] ?? '.dark' + : '@media (prefers-color-scheme: dark)'; + } + + return darkMode === 'class' + ? '.dark' + : '@media (prefers-color-scheme: dark)'; +};