feat: migrate to @nuxtjs/tailwindcss (#32)

This commit is contained in:
Benjamin Canac
2022-02-11 17:13:09 +01:00
committed by GitHub
parent 1bec8d163c
commit 702abf7a9f
20 changed files with 934 additions and 1056 deletions

View File

@@ -1,14 +1,19 @@
<template>
<div :class="name" />
<Icon :icon="name" />
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
}
<script setup>
import { Icon, addCollection } from '@iconify/vue/dist/offline'
import outline from '@iconify-json/heroicons-outline/icons.json'
import solid from '@iconify-json/heroicons-solid/icons.json'
addCollection(outline)
addCollection(solid)
defineProps({
name: {
type: String,
required: true
}
}
})
</script>

View File

@@ -1,8 +1,5 @@
<template>
<div :class="wrapperClass">
<div v-if="isLeading" :class="iconLeadingWrapperClass">
<Icon :name="iconName" :class="iconClass" />
</div>
<input
:id="name"
ref="input"
@@ -21,6 +18,9 @@
@blur="$emit('blur', $event)"
>
<slot />
<div v-if="isLeading" :class="iconLeadingWrapperClass">
<Icon :name="iconName" :class="iconClass" />
</div>
<div v-if="isTrailing" :class="iconTrailingWrapperClass">
<Icon :name="iconName" :class="iconClass" />
</div>

View File

@@ -1,9 +1,5 @@
<template>
<div :class="wrapperClass">
<div v-if="icon" :class="iconWrapperClass">
<Icon :name="icon" :class="iconClass" />
</div>
<select
:id="name"
:name="name"
@@ -36,6 +32,10 @@
/>
</template>
</select>
<div v-if="icon" :class="iconWrapperClass">
<Icon :name="icon" :class="iconClass" />
</div>
</div>
</template>
@@ -189,7 +189,7 @@ export default {
)
})
const iconWrapperClass = $ui.select.icon.leading.base
const iconWrapperClass = $ui.select.icon.leading.wrapper
return {
select,

View File

@@ -1,448 +1,165 @@
<template>
<div ref="container">
<input :value="value" :required="required" class="absolute inset-0 w-px opacity-0 cursor-default">
<Listbox
:model-value="modelValue"
as="div"
:class="wrapperClass"
@update:model-value="$emit('update:modelValue', $event)"
>
<ListboxButton :class="selectCustomClass">
<span class="block truncate">{{ modelValue[textAttribute] }}</span>
<span :class="iconWrapperClass">
<Icon name="heroicons-solid:selector" :class="iconClass" aria-hidden="true" />
</span>
</ListboxButton>
<slot :toggle="toggle" :open="open">
<TwButton
icon="solid/selector"
icon-class="u-text-gray-400"
trailing
:size="size"
:variant="variant"
base-class="w-full cursor-default focus:outline-none disabled:cursor-not-allowed disabled:opacity-75"
:disabled="disabled || !options || !options.length"
@click.native="!disabled && options && options.length && toggle()"
>
<div v-if="selectedOptions && selectedOptions.length" class="inline-flex w-full px-3 py-2 -my-2 -ml-3 truncate">
<span v-for="(selectedOption, index) of selectedOptions" :key="index" class="inline-flex items-center pr-2">
<slot name="label" :option="selectedOption">
<span class="u-text-gray-700">{{ selectedOption[textAttribute] }}</span>
</slot>
</span>
</div>
<div v-else class="inline-flex w-full u-text-gray-400">
{{ placeholder || '' }}
</div>
</TwButton>
</slot>
<transition
enter-class=""
enter-active-class=""
enter-to-class=""
leave-class="opacity-100"
leave-active-class="transition duration-100 ease-in"
leave-to-class="opacity-0"
>
<div v-show="open" ref="tooltip" class="z-10 overflow-hidden bg-white rounded-md shadow-lg dark:bg-gray-800 ring-1 u-ring-gray-200" :class="dropdownClass">
<div v-if="searchable" class="w-full border-b u-border-gray-200">
<TwInput
ref="search"
v-model="q"
type="search"
:name="`select-search-${name}`"
block
autocomplete="off"
appearance="none"
:placeholder="placeholderSearch"
/>
</div>
<ul
ref="options"
tabindex="-1"
role="listbox"
class="overflow-y-auto max-h-60 sm:text-sm focus:outline-none"
<transition leave-active-class="transition ease-in duration-100" leave-from-class="opacity-100" leave-to-class="opacity-0">
<ListboxOptions class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 u-ring-gray-200 overflow-auto focus:outline-none sm:text-sm">
<ListboxOption
v-for="(option, index) in options"
v-slot="{ active, selected, disabled }"
:key="index"
as="template"
:value="option"
:disabled="option.disabled"
>
<li
v-if="showNewOption"
ref="option-new"
role="option"
class="relative pl-3 pr-12 cursor-default select-none group hover:text-white hover:bg-primary-600"
:class="{
'bg-primary-600 text-white': active === -1,
'u-text-gray-900': active !== -1,
'py-2': dropdownSize === 'md',
'py-1 text-sm': dropdownSize === 'sm'
}"
@mouseover="active = -1"
@click="active === -1 && newOption()"
>
<slot name="newOption" :optionName="q">
<span class="block truncate">Add new option: "{{ q }}"</span>
</slot>
</li>
<li
v-for="(option, index) in filteredNormalizedOptions"
:key="index"
:ref="`option-${index}`"
role="option"
class="relative pl-3 pr-12 cursor-default select-none group hover:text-white hover:bg-primary-600"
:class="{
'font-semibold': isOptionSelected(option),
'bg-primary-600 text-white': active === index,
'u-text-gray-900': active !== index,
'py-2': dropdownSize === 'md',
'py-1 text-sm': dropdownSize === 'sm'
}"
@mouseover="active = index"
@click.prevent="active === index && selectOption(option)"
>
<slot name="option" :option="option">
<span class="block truncate">{{ option[textAttribute] }}</span>
</slot>
<span class="absolute inset-y-0 right-0 flex items-center pr-3">
<Icon
v-if="isOptionSelected(option)"
name="solid/check"
class=" group-hover:text-white"
:class="{
'text-white': active === index,
'text-primary-600': active !== index,
'h-5 w-5': dropdownSize === 'md',
'h-4 w-4': dropdownSize === 'sm'
}"
/>
<li :class="resolveOptionClass({ active, disabled })">
<span :class="[selected ? 'font-semibold' : 'font-normal', 'block truncate']">
<slot name="option" :option="option">
{{ option[textAttribute] }}
</slot>
</span>
<span v-if="selected" :class="resolveOptionIconClass({ active })">
<Icon name="heroicons-solid:check" :class="listOptionIconSizeClass" aria-hidden="true" />
</span>
</li>
</ul>
</div>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>
<script>
import { get } from 'lodash-es'
import { createPopper } from '@popperjs/core'
// import { directive as onClickaway } from 'vue-clickaway'
<script setup>
import { computed } from 'vue'
import {
Listbox,
ListboxButton,
ListboxOptions,
ListboxOption
} from '@headlessui/vue'
import Icon from '../elements/Icon'
import { classNames } from '../../utils'
import $ui from '#build/ui'
export default {
components: {
Icon
const props = defineProps({
modelValue: {
type: [String, Number, Object],
default: ''
},
// directives: {
// onClickaway
// },
shortcuts: {
disabled () {
return !this.open
},
up: 'prev',
down: 'next',
enter: 'enter',
esc: {
handler: 'close',
stop: true,
prevent: true
options: {
type: Array,
default: () => []
},
size: {
type: String,
default: 'md',
validator (value) {
return Object.keys($ui.selectCustom.size).includes(value)
}
},
props: {
value: {
type: [String, Number, Object, Array],
default: ''
},
name: {
type: String,
required: true
},
multiple: {
type: Boolean,
default: false
},
options: {
type: Array,
default: () => []
},
textAttribute: {
type: String,
default: 'text'
},
valueAttribute: {
type: String,
default: 'value'
},
searchAttributes: {
type: Array,
default: null
},
disabled: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: null
},
placeholderSearch: {
type: String,
default: 'Search...'
},
searchable: {
type: Boolean,
default: false
},
required: {
type: Boolean,
default: false
},
newEnabled: {
type: Boolean,
default: false
},
size: {
type: String,
default: 'md',
validator (value) {
return ['xxs', 'xs', 'sm', 'md', 'lg', 'xl'].includes(value)
}
},
dropdownClass: {
type: String,
default: 'w-full'
},
dropdownSize: {
type: String,
default: 'md',
validator (value) {
return ['sm', 'md'].includes(value)
}
},
variant: {
type: String,
default: 'gray'
},
strategy: {
type: String,
default: 'absolute'
},
placement: {
type: String,
default: 'bottom-start'
},
unselectable: {
type: Boolean,
default: false
}
wrapperClass: {
type: String,
default: () => $ui.selectCustom.wrapper
},
data () {
return {
open: false,
active: 0,
q: '',
instance: null
}
baseClass: {
type: String,
default: () => $ui.selectCustom.base
},
computed: {
showNewOption () {
return this.newEnabled && this.q && !this.filteredNormalizedOptions.find(option => option[this.textAttribute].toLowerCase() === this.q.toLowerCase())
},
selectedOptions () {
if (this.multiple) {
return this.value.map(value => this.normalizedOptions.find(option => option[this.valueAttribute] === value)).filter(Boolean)
} else {
return [this.normalizedOptions.find(option => option[this.valueAttribute] === this.value)].filter(Boolean)
}
},
normalizedOptions () {
return this.options.map(option => this.normalizeOption(option))
},
filteredNormalizedOptions () {
let filteredNormalizedOptions = this.normalizedOptions
if (!this.q) {
return filteredNormalizedOptions
}
try {
filteredNormalizedOptions = this.normalizedOptions.filter((option) => {
return (this.searchAttributes?.length ? this.searchAttributes : [this.textAttribute]).some((searchAttribute) => {
return option[searchAttribute] && option[searchAttribute].search(new RegExp(this.q, 'i')) !== -1
})
})
} catch (e) {}
return filteredNormalizedOptions
}
iconBaseClass: {
type: String,
default: () => $ui.selectCustom.icon.base
},
watch: {
disabled (value) {
if (value && open) { this.close() }
},
open (value) {
this.$emit('open', value)
if (!value) {
return
}
if (this.searchable) {
this.$nextTick(() => {
this.$refs.search.$refs.input.focus()
this.$refs.search.$refs.input.select()
})
}
if (this.multiple) {
if (this.value.length) {
this.active = this.filteredNormalizedOptions.findIndex(option => this.value.includes(option[this.valueAttribute]))
}
} else if (this.value) {
this.active = this.filteredNormalizedOptions.findIndex(option => option[this.valueAttribute] === this.value)
}
if (this.instance) {
this.instance.destroy()
this.instance = null
}
this.instance = createPopper(this.$refs.container, this.$refs.tooltip, {
strategy: this.strategy,
placement: this.placement,
modifiers: [
{
name: 'offset',
options: {
offset: [0, 8]
}
},
{
name: 'computeStyles',
options: {
gpuAcceleration: false,
adaptive: false
}
},
{
name: 'preventOverflow',
options: {
padding: 8
}
}
]
})
this.$nextTick(() => {
this.scrollIntoView()
})
},
filteredNormalizedOptions () {
this.updateActive()
},
q () {
this.updateActive()
}
customClass: {
type: String,
default: null
},
beforeDestroy () {
if (this.instance) {
this.instance.destroy()
this.instance = null
}
listBaseClass: {
type: String,
default: () => $ui.selectCustom.list.base
},
methods: {
toggle () {
this.open = !this.open
},
close () {
this.open = false
},
newOption () {
this.$emit('new', this.q)
},
isOptionSelected (option) {
if (this.multiple) {
return this.value && this.value.find(it => it === option[this.valueAttribute])
}
return this.value && this.value === option[this.valueAttribute]
},
selectOption (option) {
if (this.multiple) {
const value = [...this.value]
const index = value.findIndex(it => it === option[this.valueAttribute])
if (index > -1) {
value.splice(index, 1)
} else {
value.push(option[this.valueAttribute])
}
this.$emit('input', value)
} else {
if (this.isOptionSelected(option)) {
if (this.unselectable) {
this.$emit('input', null)
}
} else {
this.$emit('input', option[this.valueAttribute])
}
this.open = false
}
},
guessOptionValue (option) {
return get(option, this.valueAttribute, get(option, this.textAttribute))
},
guessOptionText (option) {
return get(option, this.textAttribute, get(option, this.valueAttribute))
},
normalizeOption (option) {
if (['string', 'number', 'boolean'].includes(typeof option)) {
return {
[this.valueAttribute]: option,
[this.textAttribute]: option
}
}
return {
...option,
[this.valueAttribute]: this.guessOptionValue(option),
[this.textAttribute]: this.guessOptionText(option)
}
},
prev () {
if (this.active - 1 >= (this.showNewOption ? -1 : 0)) {
this.active--
}
this.scrollIntoView()
},
next () {
if (this.active + 1 <= (this.filteredNormalizedOptions.length - 1)) {
this.active++
}
this.scrollIntoView()
},
enter () {
if (this.active === -1) {
if (this.showNewOption) {
this.newOption()
}
return
}
const option = this.filteredNormalizedOptions[this.active]
if (!option) {
return
}
this.selectOption(option)
},
scrollIntoView () {
let child
if (this.active === -1) {
child = this.$refs['option-new']
} else {
child = this.$refs[`option-${this.active}`][0]
}
if (!child) {
return
}
child.scrollIntoView({ block: 'nearest' })
},
updateActive () {
this.active = this.showNewOption && !this.filteredNormalizedOptions.length ? -1 : 0
}
listOptionBaseClass: {
type: String,
default: () => $ui.selectCustom.list.option.base
},
listOptionActiveClass: {
type: String,
default: () => $ui.selectCustom.list.option.active
},
listOptionInactiveClass: {
type: String,
default: () => $ui.selectCustom.list.option.inactive
},
listOptionDisabledClass: {
type: String,
default: () => $ui.selectCustom.list.option.disabled
},
listOptionIconBaseClass: {
type: String,
default: () => $ui.selectCustom.list.option.icon.base
},
listOptionIconActiveClass: {
type: String,
default: () => $ui.selectCustom.list.option.icon.active
},
listOptionIconInactiveClass: {
type: String,
default: () => $ui.selectCustom.list.option.icon.inactive
},
listOptionIconSizeClass: {
type: String,
default: () => $ui.selectCustom.list.option.icon.size
},
textAttribute: {
type: String,
default: 'text'
}
})
defineEmits(['update:modelValue'])
const selectCustomClass = computed(() => {
return classNames(
props.baseClass,
$ui.selectCustom.size[props.size],
$ui.selectCustom.spacing[props.size],
$ui.selectCustom.appearance.default,
$ui.selectCustom.trailing.spacing[props.size],
props.customClass
)
})
const iconClass = computed(() => {
return classNames(
props.iconBaseClass,
$ui.selectCustom.icon.size[props.size],
$ui.selectCustom.icon.trailing.spacing[props.size]
)
})
const iconWrapperClass = $ui.selectCustom.icon.trailing.wrapper
function resolveOptionClass ({ active, disabled }) {
return classNames(
props.listOptionBaseClass,
active ? props.listOptionActiveClass : props.listOptionInactiveClass,
disabled && props.listOptionDisabledClass
)
}
function resolveOptionIconClass ({ active }) {
return classNames(
props.listOptionIconBaseClass,
active ? props.listOptionIconActiveClass : props.listOptionIconInactiveClass
)
}
</script>

View File

@@ -1,148 +0,0 @@
[multiple],
[type=date],
[type=datetime-local],
[type=email],
[type=month],
[type=number],
[type=password],
[type=search],
[type=tel],
[type=text],
[type=time],
[type=url],
[type=week],
select,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #6b7280;
opacity: 1
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #6b7280;
opacity: 1
}
input::placeholder,
textarea::placeholder {
color: #6b7280;
opacity: 1
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0
}
::-webkit-date-and-time-value {
min-height: 1.5em
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right .5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
color-adjust: exact
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: .75rem;
-webkit-print-color-adjust: unset;
color-adjust: unset
}
[type=checkbox],
[type=radio] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
border-width: 1px;
}
[type=checkbox] {
border-radius: 0
}
[type=radio] {
border-radius: 100%
}
[type=checkbox]:checked,
[type=radio]:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat
}
[type=checkbox]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")
}
[type=radio]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")
}
[type=checkbox]:checked:focus,
[type=checkbox]:checked:hover,
[type=radio]:checked:focus,
[type=radio]:checked:hover {
border-color: transparent;
background-color: currentColor
}
[type=checkbox]:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat
}
[type=checkbox]:indeterminate:focus,
[type=checkbox]:indeterminate:hover {
border-color: transparent;
background-color: currentColor
}
[type=file] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit
}
[type=file]:focus {
outline: 1px auto -webkit-focus-ring-color
}

View File

@@ -1,5 +1,5 @@
import { defineNuxtPlugin } from '#app'
import { ClipboardPlugin } from '../types/clipboard'
import { ClipboardPlugin } from '../types'
export default defineNuxtPlugin((nuxtApp) => {
function copy (text: string, success: { title?: string, description?: string } = {}, failure: { title?: string, description?: string } = {}) {

View File

@@ -1,7 +1,7 @@
import { nanoid } from 'nanoid'
import { Ref } from 'vue'
import { defineNuxtPlugin, useState } from '#app'
import { ToastNotification, ToastPlugin } from '../types/toast'
import { ToastNotification, ToastPlugin } from '../types'
export default defineNuxtPlugin((nuxtApp) => {
const notifications: Ref<ToastNotification[]> = useState('notifications', () => [])

View File

@@ -119,7 +119,7 @@ const formGroup = {
const input = {
wrapper: 'relative',
base: 'block w-full u-bg-white u-text-gray-700 disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
base: 'relative block w-full u-bg-white u-text-gray-700 disabled:cursor-not-allowed disabled:opacity-75 focus:outline-none',
size: {
xxs: 'text-xs',
xs: 'text-xs',
@@ -204,6 +204,26 @@ const select = {
...input
}
const selectCustom = {
...select,
base: `${select.base} text-left cursor-default`,
list: {
base: 'absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 u-ring-gray-200 overflow-auto focus:outline-none sm:text-sm',
option: {
base: 'cursor-default select-none relative py-2 pl-4 pr-10',
active: 'text-white bg-primary-600',
inactive: 'u-text-gray-900',
disabled: 'cursor-not-allowed opacity-50',
icon: {
base: 'absolute inset-y-0 right-0 flex items-center pr-4',
active: 'text-white',
inactive: 'text-primary-600',
size: 'h-5 w-5'
}
}
}
}
const radio = {
wrapper: 'relative flex items-start',
base: 'h-4 w-4 text-primary-600 focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 dark:checked:border-primary-600 disabled:opacity-50 disabled:cursor-not-allowed',
@@ -281,7 +301,7 @@ const alertDialog = {
const dropdown = {
wrapper: 'relative inline-flex text-left',
container: 'w-48 z-20',
base: 'u-bg-white divide-y u-divide-gray-100 rounded-md ring-1 ring-black ring-opacity-5',
base: 'u-bg-white divide-y u-divide-gray-100 rounded-md ring-1 u-ring-gray-200 shadow-lg',
item: {
base: 'group flex items-center px-4 py-2 text-sm w-full',
active: 'u-bg-gray-100 u-text-gray-900',
@@ -299,6 +319,7 @@ export default {
input,
textarea,
select,
selectCustom,
checkbox,
radio,
container,

View File

@@ -30,6 +30,10 @@ const select = {
...input
}
const selectCustom = {
...input
}
const radio = {
base: 'h-4 w-4 u-text-gray-900 focus:ring-2 focus:ring-offset-2 focus:u-ring-gray-900 focus:ring-offset-white dark:focus:ring-offset-black u-border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed'
}
@@ -53,6 +57,7 @@ export default defu({
input,
textarea,
select,
selectCustom,
radio,
checkbox,
toggle,

63
src/runtime/tailwind.css Normal file
View File

@@ -0,0 +1,63 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.u-bg-white { @apply bg-white dark:bg-black; }
.u-bg-gray-50 { @apply bg-gray-50 dark:bg-gray-900; }
.u-bg-gray-100 { @apply bg-gray-100 dark:bg-gray-800; }
.u-bg-gray-200 { @apply bg-gray-200 dark:bg-gray-700; }
.u-bg-gray-300 { @apply bg-gray-300 dark:bg-gray-600; }
.u-bg-gray-400 { @apply bg-gray-400 dark:bg-gray-500; }
.u-bg-gray-500 { @apply bg-gray-500 dark:bg-gray-400; }
.u-bg-gray-600 { @apply bg-gray-600 dark:bg-gray-300; }
.u-bg-gray-700 { @apply bg-gray-700 dark:bg-gray-200; }
.u-bg-gray-800 { @apply bg-gray-800 dark:bg-gray-100; }
.u-bg-gray-900 { @apply bg-gray-900 dark:bg-gray-50; }
.u-bg-black { @apply bg-black dark:bg-white; }
.u-text-white { @apply text-white dark:text-black; }
.u-text-gray-50 { @apply text-gray-50 dark:text-gray-900; }
.u-text-gray-100 { @apply text-gray-100 dark:text-gray-800; }
.u-text-gray-200 { @apply text-gray-200 dark:text-gray-700; }
.u-text-gray-300 { @apply text-gray-300 dark:text-gray-600; }
.u-text-gray-400 { @apply text-gray-400 dark:text-gray-500; }
.u-text-gray-500 { @apply text-gray-500 dark:text-gray-400; }
.u-text-gray-600 { @apply text-gray-600 dark:text-gray-300; }
.u-text-gray-700 { @apply text-gray-700 dark:text-gray-200; }
.u-text-gray-800 { @apply text-gray-800 dark:text-gray-100; }
.u-text-gray-900 { @apply text-gray-900 dark:text-gray-50; }
.u-text-black { @apply text-black dark:text-white; }
.u-border-white { @apply border-white dark:border-black; }
.u-border-gray-100 { @apply border-gray-100 dark:border-gray-900; }
.u-border-gray-200 { @apply border-gray-200 dark:border-gray-800; }
.u-border-gray-300 { @apply border-gray-300 dark:border-gray-700; }
.u-border-gray-400 { @apply border-gray-400 dark:border-gray-600; }
.u-border-gray-500 { @apply border-gray-500 dark:border-gray-500; }
.u-border-gray-600 { @apply border-gray-600 dark:border-gray-400; }
.u-border-gray-700 { @apply border-gray-700 dark:border-gray-300; }
.u-border-gray-800 { @apply border-gray-800 dark:border-gray-200; }
.u-border-gray-900 { @apply border-gray-900 dark:border-gray-100; }
.u-border-black { @apply border-black dark:border-white; }
.u-divide-white { @apply divide-white dark:divide-black; }
.u-divide-gray-100 { @apply divide-gray-100 dark:divide-gray-900; }
.u-divide-gray-200 { @apply divide-gray-200 dark:divide-gray-800; }
.u-divide-gray-300 { @apply divide-gray-300 dark:divide-gray-700; }
.u-divide-gray-400 { @apply divide-gray-400 dark:divide-gray-600; }
.u-divide-gray-500 { @apply divide-gray-500 dark:divide-gray-500; }
.u-divide-gray-600 { @apply divide-gray-600 dark:divide-gray-400; }
.u-divide-gray-700 { @apply divide-gray-700 dark:divide-gray-300; }
.u-divide-gray-800 { @apply divide-gray-800 dark:divide-gray-200; }
.u-divide-gray-900 { @apply divide-gray-900 dark:divide-gray-100; }
.u-divide-black { @apply divide-black dark:divide-white; }
.u-ring-white { @apply ring-white dark:ring-black; }
.u-ring-gray-100 { @apply ring-gray-100 dark:ring-gray-900; }
.u-ring-gray-200 { @apply ring-gray-200 dark:ring-gray-800; }
.u-ring-gray-300 { @apply ring-gray-300 dark:ring-gray-700; }
.u-ring-gray-400 { @apply ring-gray-400 dark:ring-gray-600; }
.u-ring-gray-500 { @apply ring-gray-500 dark:ring-gray-500; }
.u-ring-gray-600 { @apply ring-gray-600 dark:ring-gray-400; }
.u-ring-gray-700 { @apply ring-gray-700 dark:ring-gray-300; }
.u-ring-gray-800 { @apply ring-gray-800 dark:ring-gray-200; }
.u-ring-gray-900 { @apply ring-gray-900 dark:ring-gray-100; }
.u-ring-black { @apply ring-black dark:ring-white; }
}

2
src/runtime/types/index.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
export * from './clipboard'
export * from './toast'