feat(ui): color (see "using color" in dev:ui-docs)
This commit is contained in:
		
							parent
							
								
									925d2db0a6
								
							
						
					
					
						commit
						e583c51a54
					
				| 
						 | 
					@ -52,6 +52,7 @@
 | 
				
			||||||
    "standardized-audio-context": "25.3.60",
 | 
					    "standardized-audio-context": "25.3.60",
 | 
				
			||||||
    "text-clipper": "2.2.0",
 | 
					    "text-clipper": "2.2.0",
 | 
				
			||||||
    "transliteration": "2.3.5",
 | 
					    "transliteration": "2.3.5",
 | 
				
			||||||
 | 
					    "type-fest": "4.30.1",
 | 
				
			||||||
    "universal-cookie": "4.0.4",
 | 
					    "universal-cookie": "4.0.4",
 | 
				
			||||||
    "vite-plugin-pwa": "0.14.4",
 | 
					    "vite-plugin-pwa": "0.14.4",
 | 
				
			||||||
    "vue": "3.5.13",
 | 
					    "vue": "3.5.13",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,15 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { useColorOrPastel, type PastelProps } from '~/composables/colors'
 | 
					import { type PastelProps, propsToColor } from '~/composables/colors';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<PastelProps>()
 | 
					type Props = PastelProps
 | 
				
			||||||
const color = useColorOrPastel(() => props.color, 'blue')
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps<Props>()
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div
 | 
					  <div
 | 
				
			||||||
    class="funkwhale is-colored alert"
 | 
					    class="funkwhale is-colored solid alert"
 | 
				
			||||||
    :class="[color]"
 | 
					    v-bind="propsToColor(props)"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <slot />
 | 
					    <slot />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { ref, computed, useSlots, onMounted } from 'vue'
 | 
					import { ref, computed, useSlots, onMounted } from 'vue'
 | 
				
			||||||
import { type ColorProps, useColor } from '~/composables/colors'
 | 
					import { type ColorProps, type VariantProps, propsToColor } from '~/composables/colors';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Loader from '~/components/ui/Loader.vue'
 | 
					import Loader from '~/components/ui/Loader.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Props {
 | 
					type Props = {
 | 
				
			||||||
  variant?: 'solid' | 'outline' | 'ghost'
 | 
					 | 
				
			||||||
  width?: 'standard' | 'auto' | 'full'
 | 
					  width?: 'standard' | 'auto' | 'full'
 | 
				
			||||||
  alignText?: 'left' | 'center' | 'right'
 | 
					  alignText?: 'left' | 'center' | 'right'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,14 +18,12 @@ interface Props {
 | 
				
			||||||
  onClick?: (...args: any[]) => void | Promise<void>
 | 
					  onClick?: (...args: any[]) => void | Promise<void>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  autofocus? : boolean
 | 
					  autofocus? : boolean
 | 
				
			||||||
}
 | 
					} & ColorProps & VariantProps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<Props & ColorProps>()
 | 
					const props = defineProps<Props>()
 | 
				
			||||||
 | 
					 | 
				
			||||||
const color = useColor(() => props.color)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const slots = useSlots()
 | 
					const slots = useSlots()
 | 
				
			||||||
const iconOnly = computed(() => !!props.icon && !slots.default)
 | 
					const isIconOnly = computed(() => !!props.icon && !slots.default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const internalLoader = ref(false)
 | 
					const internalLoader = ref(false)
 | 
				
			||||||
const isLoading = computed(() => props.isLoading || internalLoader.value)
 | 
					const isLoading = computed(() => props.isLoading || internalLoader.value)
 | 
				
			||||||
| 
						 | 
					@ -48,20 +45,22 @@ onMounted(() => {
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <button ref="button" class="funkwhale is-colored button" :class="[
 | 
					  <button ref="button"
 | 
				
			||||||
    color,
 | 
					    v-bind="propsToColor({...props, interactive:true})"
 | 
				
			||||||
    'is-' + (variant ?? 'solid'),
 | 
					    class="funkwhale is-colored button"
 | 
				
			||||||
    'is-' + (width ?? 'standard'),
 | 
					    :class="[
 | 
				
			||||||
    'is-aligned-' + (alignText ?? 'center'),
 | 
					      'is-' + (width ?? 'standard'),
 | 
				
			||||||
    {
 | 
					      'is-aligned-' + (alignText ?? 'center'),
 | 
				
			||||||
      'is-active': isActive,
 | 
					      {
 | 
				
			||||||
      'is-loading': isLoading,
 | 
					        'is-active': isActive,
 | 
				
			||||||
      'icon-only': iconOnly,
 | 
					        'is-loading': isLoading,
 | 
				
			||||||
      'has-icon': !!icon,
 | 
					        'is-icon-only': isIconOnly,
 | 
				
			||||||
      'is-round': round,
 | 
					        'has-icon': !!icon,
 | 
				
			||||||
      'is-shadow': shadow
 | 
					        'is-round': round,
 | 
				
			||||||
    }
 | 
					        'is-shadow': shadow
 | 
				
			||||||
  ]" @click="click">
 | 
					      }
 | 
				
			||||||
 | 
					    ]" @click="click"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
    <i v-if="icon" :class="['bi', icon]" />
 | 
					    <i v-if="icon" :class="['bi', icon]" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <span>
 | 
					    <span>
 | 
				
			||||||
| 
						 | 
					@ -73,5 +72,81 @@ onMounted(() => {
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss">
 | 
					<style lang="scss">
 | 
				
			||||||
@import './button.scss'
 | 
					.funkwhale {
 | 
				
			||||||
 | 
					  &.button {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    display: inline-flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    font-family: $font-main;
 | 
				
			||||||
 | 
					    font-weight: 900;
 | 
				
			||||||
 | 
					    font-size: 0.875em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    line-height: 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    padding: 0.642857142857em 0.714em 0.714em 0.714em;
 | 
				
			||||||
 | 
					    &.is-icon-only {
 | 
				
			||||||
 | 
					      padding: 0.675em 0.714em 0.678em 0.714em;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    border-radius: var(--fw-border-radius);
 | 
				
			||||||
 | 
					    margin: 0 0.5ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    transform: translateX(var(--fw-translate-x)) translateY(var(--fw-translate-y)) scale(var(--fw-scale));
 | 
				
			||||||
 | 
					    transition: all .2s ease;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.is-aligned-center {
 | 
				
			||||||
 | 
					      justify-content: center;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.is-aligned-left {
 | 
				
			||||||
 | 
					      justify-content: flex-start;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.is-aligned-right {
 | 
				
			||||||
 | 
					      justify-content: flex-end;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.is-shadow {
 | 
				
			||||||
 | 
					      box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:not(.is-icon-only):not(.is-auto) {
 | 
				
			||||||
 | 
					      min-width: 8.5rem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.is-full {
 | 
				
			||||||
 | 
					      display: block;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.is-round {
 | 
				
			||||||
 | 
					      border-radius: 100vh;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &[disabled] {
 | 
				
			||||||
 | 
					      font-weight: normal;
 | 
				
			||||||
 | 
					      pointer-events: none;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.is-loading {
 | 
				
			||||||
 | 
					      @extend .is-active;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      > span {
 | 
				
			||||||
 | 
					        opacity: 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    i.bi {
 | 
				
			||||||
 | 
					      font-size: 1.2rem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    i.bi + span:not(:empty) {
 | 
				
			||||||
 | 
					      margin-left: 1ch;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,8 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { useCssModule } from 'vue'
 | 
					 | 
				
			||||||
import { computed } from 'vue'
 | 
					import { computed } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { type RouterLinkProps, RouterLink } from 'vue-router';
 | 
					import { type RouterLinkProps, RouterLink } from 'vue-router';
 | 
				
			||||||
import { type Pastel } from '~/composables/colors';
 | 
					import { type ColorProps, type PastelProps, type RaisedProps, type VariantProps, propsToColor } from '~/composables/colors';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Pill from './Pill.vue'
 | 
					import Pill from './Pill.vue'
 | 
				
			||||||
import Alert from './Alert.vue'
 | 
					import Alert from './Alert.vue'
 | 
				
			||||||
| 
						 | 
					@ -13,12 +12,11 @@ import Spacer from './layout/Spacer.vue';
 | 
				
			||||||
interface Props extends Partial<RouterLinkProps> {
 | 
					interface Props extends Partial<RouterLinkProps> {
 | 
				
			||||||
  title: string
 | 
					  title: string
 | 
				
			||||||
  category?: true | "h1" | "h2" | "h3" | "h4" | "h5"
 | 
					  category?: true | "h1" | "h2" | "h3" | "h4" | "h5"
 | 
				
			||||||
  color?: Pastel
 | 
					 | 
				
			||||||
  image?: string | { src: string, style?: "withPadding" }
 | 
					  image?: string | { src: string, style?: "withPadding" }
 | 
				
			||||||
  tags?: string[]
 | 
					  tags?: string[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<Props>()
 | 
					const props = defineProps<Props & (PastelProps | ColorProps) & RaisedProps & VariantProps>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const image = typeof props.image === 'string' ? { src: props.image } : props.image
 | 
					const image = typeof props.image === 'string' ? { src: props.image } : props.image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,7 +144,11 @@ const isExternalLink = computed(() => {
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Layout stack :class="{ [$style.card]: true, [$style['is-category']]: category }" style="--gap:16px">
 | 
					  <Layout stack
 | 
				
			||||||
 | 
					    :class="{ [$style.card]: true, [$style['is-category']]: category }"
 | 
				
			||||||
 | 
					    style="--gap:16px"
 | 
				
			||||||
 | 
					    v-bind="propsToColor({...(props.to? { interactive: true, solid: true, default: true } : {}), ...props})"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Link -->
 | 
					    <!-- Link -->
 | 
				
			||||||
    <a v-if="props.to && isExternalLink" :class="$style.covering" :href="to?.toString()" target="_blank" />
 | 
					    <a v-if="props.to && isExternalLink" :class="$style.covering" :href="to?.toString()" target="_blank" />
 | 
				
			||||||
| 
						 | 
					@ -164,7 +166,7 @@ const isExternalLink = computed(() => {
 | 
				
			||||||
    <!-- Content -->
 | 
					    <!-- Content -->
 | 
				
			||||||
    <component :class="$style.title" :is="typeof category === 'string' ? category : 'h6'">{{ title }}</component>
 | 
					    <component :class="$style.title" :is="typeof category === 'string' ? category : 'h6'">{{ title }}</component>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <Alert v-if="$slots.alert" :class="$style.alert">
 | 
					    <Alert blue v-if="$slots.alert" :class="$style.alert">
 | 
				
			||||||
      <slot name="alert" />
 | 
					      <slot name="alert" />
 | 
				
			||||||
    </Alert>
 | 
					    </Alert>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,34 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { computed } from 'vue';
 | 
					import { computed } from 'vue';
 | 
				
			||||||
import { type RouterLinkProps, RouterLink } from 'vue-router';
 | 
					import { type RouterLinkProps, RouterLink } from 'vue-router';
 | 
				
			||||||
import { type ColorProps, useColor } from '~/composables/colors';
 | 
					import { type ColorProps, propsToColor } from '~/composables/colors';
 | 
				
			||||||
const { to, icon, color, inline } = defineProps<RouterLinkProps & ColorProps & {
 | 
					const { to, icon, inline, ...otherProps } = defineProps<RouterLinkProps
 | 
				
			||||||
 | 
					  & ColorProps
 | 
				
			||||||
 | 
					  & {
 | 
				
			||||||
    icon?: string;
 | 
					    icon?: string;
 | 
				
			||||||
    inline?: true
 | 
					    inline?: true
 | 
				
			||||||
  }>()
 | 
					  }>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const colorClass = useColor(() => color)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const isExternalLink = computed(() => {
 | 
					const isExternalLink = computed(() => {
 | 
				
			||||||
  return typeof to === 'string' && to.startsWith('http')
 | 
					  return typeof to === 'string' && to.startsWith('http')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <a v-if="isExternalLink" :class="[$style.external, colorClass, color && 'is-colored', inline && $style.inline]" :href="to?.toString()" target="_blank">
 | 
					  <a v-if="isExternalLink"
 | 
				
			||||||
      <slot />
 | 
					    :v-bind="propsToColor(otherProps)"
 | 
				
			||||||
 | 
					    :class="[$style.link, $style.external, inline && $style.inline]"
 | 
				
			||||||
 | 
					    :href="to?.toString()"
 | 
				
			||||||
 | 
					    target="_blank"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <i v-if="icon" :class="['bi', icon]" />
 | 
				
			||||||
 | 
					    <slot />
 | 
				
			||||||
  </a>
 | 
					  </a>
 | 
				
			||||||
  <RouterLink v-if="to && !isExternalLink" :to="to" :class="[colorClass, color && 'is-colored', inline && $style.inline]">
 | 
					  <RouterLink v-else
 | 
				
			||||||
 | 
					    :to="to"
 | 
				
			||||||
 | 
					    :v-bind="propsToColor(otherProps)"
 | 
				
			||||||
 | 
					    :class="[$style.link, inline && $style.inline]"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
    <i v-if="icon" :class="['bi', icon]" />
 | 
					    <i v-if="icon" :class="['bi', icon]" />
 | 
				
			||||||
    <slot />
 | 
					    <slot />
 | 
				
			||||||
  </RouterLink>
 | 
					  </RouterLink>
 | 
				
			||||||
| 
						 | 
					@ -28,7 +38,7 @@ const isExternalLink = computed(() => {
 | 
				
			||||||
  .active { outline: 3px solid red; }
 | 
					  .active { outline: 3px solid red; }
 | 
				
			||||||
  .external { outline: 3px dotted blue; }
 | 
					  .external { outline: 3px dotted blue; }
 | 
				
			||||||
  .inline { display:inline-flex; }
 | 
					  .inline { display:inline-flex; }
 | 
				
			||||||
  a {
 | 
					  .link {
 | 
				
			||||||
    background-color: var(--fw-bg-color);
 | 
					    background-color: var(--fw-bg-color);
 | 
				
			||||||
    color: var(--fw-text-color);
 | 
					    color: var(--fw-text-color);
 | 
				
			||||||
    border: 1px solid var(--fw-bg-color);
 | 
					    border: 1px solid var(--fw-bg-color);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ const isOpen = defineModel<boolean>({ default:false })
 | 
				
			||||||
        <div @click.stop class="funkwhale modal" :class="$slots.alert && 'has-alert'" >
 | 
					        <div @click.stop class="funkwhale modal" :class="$slots.alert && 'has-alert'" >
 | 
				
			||||||
          <h2>
 | 
					          <h2>
 | 
				
			||||||
            {{ title }}
 | 
					            {{ title }}
 | 
				
			||||||
            <Button icon="bi-x-lg" color="secondary" variant="ghost" @click="isOpen = false" />
 | 
					            <Button icon="bi-x-lg" ghost @click="isOpen = false" />
 | 
				
			||||||
          </h2>
 | 
					          </h2>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="modal-content">
 | 
					          <div class="modal-content">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,20 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { useColorOrPastel, type ColorProps, type PastelProps } from '~/composables/colors'
 | 
					import { type ColorProps, type PastelProps, propsToColor } from '~/composables/colors';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<ColorProps | PastelProps>()
 | 
					 | 
				
			||||||
const color = useColorOrPastel(() => props.color, 'secondary')
 | 
					 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
  click: [event: MouseEvent]
 | 
					  click: [event: MouseEvent]
 | 
				
			||||||
}>()
 | 
					}>()
 | 
				
			||||||
const handleClick = (event: MouseEvent) => {
 | 
					const handleClick = (event: MouseEvent) => {
 | 
				
			||||||
  emit('click', event)
 | 
					  emit('click', event)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					const props = defineProps<PastelProps | ColorProps>()
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <button
 | 
					  <button
 | 
				
			||||||
    type="button"
 | 
					    type="button"
 | 
				
			||||||
    class="funkwhale is-colored pill"
 | 
					    class="funkwhale is-colored pill"
 | 
				
			||||||
    :class="[color]"
 | 
					    v-bind="propsToColor({...props, interactive:true})"
 | 
				
			||||||
    @click.stop="handleClick"
 | 
					    @click.stop="handleClick"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <div v-if="!!$slots.image" class="pill-image">
 | 
					    <div v-if="!!$slots.image" class="pill-image">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -186,17 +186,17 @@ const focus = () => textarea.value.focus()
 | 
				
			||||||
      @keydown.ctrl.shift.x.exact.prevent="strikethrough" @keydown.ctrl.k.exact.prevent="link" :maxlength="max"
 | 
					      @keydown.ctrl.shift.x.exact.prevent="strikethrough" @keydown.ctrl.k.exact.prevent="link" :maxlength="max"
 | 
				
			||||||
      :placeholder="placeholder" v-model="model" id="textarea_id" />
 | 
					      :placeholder="placeholder" v-model="model" id="textarea_id" />
 | 
				
			||||||
    <div class="textarea-buttons">
 | 
					    <div class="textarea-buttons">
 | 
				
			||||||
      <Button @click="preview = !preview" icon="bi-eye" color="secondary" :is-active="preview" />
 | 
					      <Button @click="preview = !preview" icon="bi-eye" color="secondary" :aria-pressed="preview" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="separator" />
 | 
					      <div class="separator" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Button @click="heading1" icon="bi-type-h1" color="secondary" :is-active="isHeading1" :disabled="preview" />
 | 
					      <Button @click="heading1" icon="bi-type-h1" color="secondary" :aria-pressed="isHeading1" :disabled="preview" />
 | 
				
			||||||
      <Button @click="heading2" icon="bi-type-h2" color="secondary" :is-active="isHeading2" :disabled="preview" />
 | 
					      <Button @click="heading2" icon="bi-type-h2" color="secondary" :aria-pressed="isHeading2" :disabled="preview" />
 | 
				
			||||||
      <Button @click="paragraph" icon="bi-paragraph" color="secondary" :is-active="isParagraph" :disabled="preview" />
 | 
					      <Button @click="paragraph" icon="bi-paragraph" color="secondary" :aria-pressed="isParagraph" :disabled="preview" />
 | 
				
			||||||
      <Button @click="quote" icon="bi-quote" color="secondary" :is-active="isQuote" :disabled="preview" />
 | 
					      <Button @click="quote" icon="bi-quote" color="secondary" :aria-pressed="isQuote" :disabled="preview" />
 | 
				
			||||||
      <Button @click="orderedList" icon="bi-list-ol" color="secondary" :is-active="isOrderedList"
 | 
					      <Button @click="orderedList" icon="bi-list-ol" color="secondary" :aria-pressed="isOrderedList"
 | 
				
			||||||
        :disabled="preview" />
 | 
					        :disabled="preview" />
 | 
				
			||||||
      <Button @click="unorderedList" icon="bi-list-ul" color="secondary" :is-active="isUnorderedList"
 | 
					      <Button @click="unorderedList" icon="bi-list-ul" color="secondary" :aria-pressed="isUnorderedList"
 | 
				
			||||||
        :disabled="preview" />
 | 
					        :disabled="preview" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="separator" />
 | 
					      <div class="separator" />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,37 +1,37 @@
 | 
				
			||||||
.funkwhale.alert {
 | 
					.funkwhale.alert {
 | 
				
			||||||
  color: var(--fw-gray-900);
 | 
					  // color: var(--fw-gray-900);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @include light-theme {
 | 
					  // @include light-theme {
 | 
				
			||||||
    background-color: var(--fw-pastel-1, var(--fw-bg-color));
 | 
					  //   background-color: var(--fw-pastel-1, var(--fw-bg-color));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    > .actions .funkwhale.button {
 | 
					  //   > .actions .funkwhale.button {
 | 
				
			||||||
      --fw-bg-color: var(--fw-pastel-2);
 | 
					  //     --fw-bg-color: var(--fw-pastel-2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &:hover, &.is-hovered {
 | 
					  //     &:hover, &.is-hovered {
 | 
				
			||||||
        --fw-bg-color: var(--fw-pastel-3);
 | 
					  //       --fw-bg-color: var(--fw-pastel-3);
 | 
				
			||||||
      }
 | 
					  //     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &:active, &.is-active {
 | 
					  //     &:active, &.is-active {
 | 
				
			||||||
        --fw-bg-color: var(--fw-pastel-4);
 | 
					  //       --fw-bg-color: var(--fw-pastel-4);
 | 
				
			||||||
      }
 | 
					  //     }
 | 
				
			||||||
    }
 | 
					  //   }
 | 
				
			||||||
  }
 | 
					  // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @include dark-theme {
 | 
					  // @include dark-theme {
 | 
				
			||||||
    background-color: var(--fw-pastel-3, var(--fw-bg-color));
 | 
					  //   background-color: var(--fw-pastel-3, var(--fw-bg-color));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    > .actions .funkwhale.button {
 | 
					  //   > .actions .funkwhale.button {
 | 
				
			||||||
      --fw-bg-color: var(--fw-pastel-4);
 | 
					  //     --fw-bg-color: var(--fw-pastel-4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &:hover, &.is-hovered {
 | 
					  //     &:hover, &.is-hovered {
 | 
				
			||||||
        --fw-bg-color: var(--fw-pastel-2);
 | 
					  //       --fw-bg-color: var(--fw-pastel-2);
 | 
				
			||||||
      }
 | 
					  //     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &:active, &.is-active {
 | 
					  //     &:active, &.is-active {
 | 
				
			||||||
        --fw-bg-color: var(--fw-pastel-1);
 | 
					  //       --fw-bg-color: var(--fw-pastel-1);
 | 
				
			||||||
      }
 | 
					  //     }
 | 
				
			||||||
    }
 | 
					  //   }
 | 
				
			||||||
  }
 | 
					  // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  padding: 0.625rem 2rem;
 | 
					  padding: 0.625rem 2rem;
 | 
				
			||||||
  line-height: 1.2;
 | 
					  line-height: 1.2;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,14 +2,10 @@
 | 
				
			||||||
  &.pill {
 | 
					  &.pill {
 | 
				
			||||||
    color: var(--fw-text-color);
 | 
					    color: var(--fw-text-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @include light-theme {
 | 
					    // @include dark-theme {
 | 
				
			||||||
      background-color: var(--fw-pastel-2, var(--fw-bg-color));
 | 
					    //   --fw-darken-pastel: color-mix(in srgb, var(--fw-pastel-4) 90%, black);
 | 
				
			||||||
    }
 | 
					    //   background-color: var(--fw-darken-pastel, var(--fw-bg-color));
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
    @include dark-theme {
 | 
					 | 
				
			||||||
      --fw-darken-pastel: color-mix(in srgb, var(--fw-pastel-4) 90%, black);
 | 
					 | 
				
			||||||
      background-color: var(--fw-darken-pastel, var(--fw-bg-color));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    position: relative;
 | 
					    position: relative;
 | 
				
			||||||
    display: inline-flex;
 | 
					    display: inline-flex;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,61 @@
 | 
				
			||||||
import { toValue, type MaybeRefOrGetter } from "@vueuse/core"
 | 
					import { toValue, type MaybeRefOrGetter } from "@vueuse/core"
 | 
				
			||||||
 | 
					import type { Entry, Join, KeysOfUnion, RequireExactlyOne, RequireOneOrNone, Simplify, SingleKeyObject, UnionToIntersection } from "type-fest"
 | 
				
			||||||
import { computed } from 'vue'
 | 
					import { computed } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function useColor(color: MaybeRefOrGetter<Color | undefined>, defaultColor: Color = 'primary') {
 | 
					export type DefaultProps =
 | 
				
			||||||
  return computed(() => `is-${toValue(color) ?? defaultColor}`)
 | 
					  | { default?: true }
 | 
				
			||||||
}
 | 
					export type Default = KeysOfUnion<DefaultProps>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function usePastel(color: MaybeRefOrGetter<Pastel | undefined>, defaultColor: Pastel = 'blue') {
 | 
					export type ColorProps =
 | 
				
			||||||
  return computed(() => `is-${toValue(color) ?? defaultColor}`)
 | 
					  | { primary?: true}
 | 
				
			||||||
}
 | 
					  | { secondary?: true }
 | 
				
			||||||
 | 
					  | { destructive?: true }
 | 
				
			||||||
 | 
					export type Color = KeysOfUnion<ColorProps>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function useColorOrPastel<T extends Color | Pastel>(color: MaybeRefOrGetter<T | undefined>, defaultColor: T) {
 | 
					export type PastelProps =
 | 
				
			||||||
  return computed(() => `is-${toValue(color) ?? defaultColor}`)
 | 
					  | { red?:true }
 | 
				
			||||||
}
 | 
					  | { blue?:true }
 | 
				
			||||||
 | 
					  | { purple?:true }
 | 
				
			||||||
 | 
					  | { green?: true }
 | 
				
			||||||
 | 
					  | { yellow?:true }
 | 
				
			||||||
 | 
					export type Pastel = KeysOfUnion<PastelProps>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Color = 'primary' | 'secondary' | 'destructive'
 | 
					export type VariantProps =
 | 
				
			||||||
export interface ColorProps {
 | 
					  | { solid?:true }
 | 
				
			||||||
  color?: Color
 | 
					  | { outline?:true }
 | 
				
			||||||
}
 | 
					  | { ghost?:true }
 | 
				
			||||||
 | 
					export type Variant = KeysOfUnion<VariantProps>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Pastel = 'red' | 'blue' | 'purple' | 'green' | 'yellow'
 | 
					export type InteractiveProps =
 | 
				
			||||||
export interface PastelProps {
 | 
					| { interactive?: true }
 | 
				
			||||||
  color?: Pastel
 | 
					export type Interactive = KeysOfUnion<DefaultProps>
 | 
				
			||||||
}
 | 
					
 | 
				
			||||||
 | 
					export type RaisedProps =
 | 
				
			||||||
 | 
					| { raised?: true }
 | 
				
			||||||
 | 
					export type Raised = KeysOfUnion<RaisedProps>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Props =
 | 
				
			||||||
 | 
					  DefaultProps & ColorProps & PastelProps & VariantProps & InteractiveProps & RaisedProps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ColorSelector =
 | 
				
			||||||
 | 
					  `${Color | Pastel | Default}${'' | ` ${Variant}${'' | ' interactive'}${'' | ' raised'}`}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Apply a color class for setting `color`, `background-color` and `border` styles.
 | 
				
			||||||
 | 
					 * You can override it with `style="..."`.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param color Choose a semantic color or the default page background.
 | 
				
			||||||
 | 
					 * This will affect the text color.
 | 
				
			||||||
 | 
					 * To add an outline or filled background, add a `Variant`: `'solid' | 'outline'`.
 | 
				
			||||||
 | 
					 * If the surface should stand out, add `raised`.
 | 
				
			||||||
 | 
					 * If the surface reacts to mouse input, add `interactive`.
 | 
				
			||||||
 | 
					 * @returns the corresponding `class` object
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: Make sure to implement the necessary classes in `colors.scss`!
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const color = (color: ColorSelector) =>
 | 
				
			||||||
 | 
					  ({ class: color })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Color from Props
 | 
				
			||||||
 | 
					export const propsToColor = (props: Partial<Props>) =>
 | 
				
			||||||
 | 
					  ({ class: Object.entries(props).filter(([key, value])=>value && key).map(([key,_])=>key).join(" ") })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,7 +87,7 @@
 | 
				
			||||||
  --fw-pastel-yellow-3: #fed100;
 | 
					  --fw-pastel-yellow-3: #fed100;
 | 
				
			||||||
  --fw-pastel-yellow-4: #efa300;
 | 
					  --fw-pastel-yellow-4: #efa300;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Override Bulma
 | 
					  // Same in light and dark theme
 | 
				
			||||||
  --fw-primary: var(--fw-blue-500);
 | 
					  --fw-primary: var(--fw-blue-500);
 | 
				
			||||||
  --fw-secondary: #ff6600;
 | 
					  --fw-secondary: #ff6600;
 | 
				
			||||||
  --fw-destructive: var(--fw-red-500);
 | 
					  --fw-destructive: var(--fw-red-500);
 | 
				
			||||||
| 
						 | 
					@ -96,83 +96,215 @@
 | 
				
			||||||
  --fw-page-bg-color: var(--fw-gray-960);
 | 
					  --fw-page-bg-color: var(--fw-gray-960);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.funkwhale {
 | 
					:is(.VPDoc .vp-doc, .funkwhale){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  :is(button, input):focus-visible {
 | 
				
			||||||
 | 
					    outline: 3px solid var(--fw-secondary);
 | 
				
			||||||
 | 
					    outline-offset: 2px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Variants
 | 
				
			||||||
 | 
					  .solid, .alert>.actions>button, button:not(:is(.ghost,.outline,.tabs-item)) {
 | 
				
			||||||
 | 
					    color: var(--color);
 | 
				
			||||||
 | 
					    background-color:var(--background-color);
 | 
				
			||||||
 | 
					    border: 1px solid var(--background-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.interactive {
 | 
				
			||||||
 | 
					      &[aria-pressed=true] {
 | 
				
			||||||
 | 
					        color: var(--pressed-color, var(--active-color));
 | 
				
			||||||
 | 
					        background-color: var(--pressed-background-color, var(--active-background-color));
 | 
				
			||||||
 | 
					        border-color: var(--pressed-background-color, var(--active-background-color));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      &:hover{
 | 
				
			||||||
 | 
					        color:var(--hover-color);
 | 
				
			||||||
 | 
					        background-color:var(--hover-background-color);
 | 
				
			||||||
 | 
					        border-color: var(--hover-background-color);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      &:active{
 | 
				
			||||||
 | 
					        color:var(--active-color);
 | 
				
			||||||
 | 
					        background-color:var(--active-background-color);
 | 
				
			||||||
 | 
					        border-color: var(--active-background-color);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      &[disabled] {
 | 
				
			||||||
 | 
					        color: var(--disabled-color);
 | 
				
			||||||
 | 
					        border-color: var(--disabled-border-color);
 | 
				
			||||||
 | 
					        background-color:var(--disabled-background-color);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .ghost {
 | 
				
			||||||
 | 
					    color: var(--color);
 | 
				
			||||||
 | 
					    border: 1px solid transparent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.interactive{
 | 
				
			||||||
 | 
					      &:hover{
 | 
				
			||||||
 | 
					        border: 1px solid var(--hover-background-color);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      &:active{
 | 
				
			||||||
 | 
					        border: 1px solid var(--active-background-color);
 | 
				
			||||||
 | 
					        &.router-link-exact-active {
 | 
				
			||||||
 | 
					          border: 1px solid var(--exact-active-background-color);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      &[disabled] {
 | 
				
			||||||
 | 
					        opacity:.5;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .outline {
 | 
				
			||||||
 | 
					    color: var(--color);
 | 
				
			||||||
 | 
					    border: 1px solid var(--border-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &.interactive{
 | 
				
			||||||
 | 
					      &:hover{
 | 
				
			||||||
 | 
					        border: 1px solid var(--hover-background-color);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      &:active{
 | 
				
			||||||
 | 
					        border: 1px solid var(--active-background-color);
 | 
				
			||||||
 | 
					        &.router-link-exact-active {
 | 
				
			||||||
 | 
					          background: 1px solid var(--exact-active-background-color);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					:root{
 | 
				
			||||||
  @include light-theme {
 | 
					  @include light-theme {
 | 
				
			||||||
    .is-primary {
 | 
					    --fw-page-bg-color: var(--fw-beige-100);
 | 
				
			||||||
      --fw-bg-color: var(--fw-blue-400);
 | 
					 | 
				
			||||||
      --fw-text-color: var(--fw-blue-010);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &.is-colored {
 | 
					    .default {
 | 
				
			||||||
        &[disabled] {
 | 
					      --color: var(--fw-gray-900);
 | 
				
			||||||
          --fw-bg-color: var(--fw-blue-100) !important;
 | 
					      --background-color: var(--fw-beige-100);
 | 
				
			||||||
          --fw-text-color: var(--fw-blue-900) !important;
 | 
					      --border-color:var(--fw-gray-300);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.is-hovered,
 | 
					      --hover-color:var(--fw-gray-800);
 | 
				
			||||||
        &:hover {
 | 
					      --hover-background-color:var(--fw-beige-200);
 | 
				
			||||||
          --fw-bg-color: var(--fw-blue-500);
 | 
					      --hover-border-color:var(--fw-gray-800);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.is-active,
 | 
					      --active-color:var(--fw-red-40);
 | 
				
			||||||
        &:active {
 | 
					      --active-background-color:var(--fw-beige-400);
 | 
				
			||||||
          --fw-bg-color: var(--fw-blue-600);
 | 
					      --active-border-color:var(--fw-gray-600);
 | 
				
			||||||
          &.router-link-exact-active {
 | 
					
 | 
				
			||||||
            --fw-bg-color: var(--fw-blue-700);
 | 
					      --pressed-color:var(--fw-red-40);
 | 
				
			||||||
          }
 | 
					      --pressed-background-color:var(--fw-gray-900);
 | 
				
			||||||
        }
 | 
					
 | 
				
			||||||
 | 
					      --disabled-color:var(--fw-gray-500);
 | 
				
			||||||
 | 
					      --disabled-background-color:var(--fw-beige-100);
 | 
				
			||||||
 | 
					      --disabled-border-color:var(--fw-beige-100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      &.raised{
 | 
				
			||||||
 | 
					        --background-color:var(--fw-beige-300);
 | 
				
			||||||
 | 
					        --border-color:var(--fw-beige-400);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .is-secondary {
 | 
					    .primary {
 | 
				
			||||||
      --fw-bg-color: var(--fw-gray-200);
 | 
					      --color: var(--fw-blue-010);
 | 
				
			||||||
      --fw-text-color: var(--fw-gray-900);
 | 
					      --background-color:var(--fw-blue-400);
 | 
				
			||||||
 | 
					      --border-color:var(--fw-blue-010);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &.is-colored {
 | 
					      --hover-color: var(--fw-blue-010);
 | 
				
			||||||
        &[disabled] {
 | 
					      --hover-background-color:var(--fw-blue-500);
 | 
				
			||||||
          --fw-bg-color: var(--fw-gray-100) !important;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.is-hovered,
 | 
					      --active-color: var(--fw-blue-010);
 | 
				
			||||||
        &:hover {
 | 
					      --active-background-color:var(--fw-blue-600);
 | 
				
			||||||
          --fw-bg-color: var(--fw-gray-200);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.is-active,
 | 
					      --pressed-color:var(--fw-blue-010);
 | 
				
			||||||
        &.active,
 | 
					      --pressed-background-color:var(--fw-blue-800);
 | 
				
			||||||
        &:active {
 | 
					
 | 
				
			||||||
          --fw-bg-color: var(--fw-gray-300);
 | 
					      --disabled-color:var(--fw-blue-900);
 | 
				
			||||||
          &.router-link-exact-active {
 | 
					      --disabled-background-color:var(--fw-blue-100);
 | 
				
			||||||
            --fw-bg-color: var(--fw-gray-500);
 | 
					      --disabled-border-color:var(--fw-blue-100);
 | 
				
			||||||
          }
 | 
					
 | 
				
			||||||
        }
 | 
					      &.raised {
 | 
				
			||||||
 | 
					        --background-color:var(--fw-blue-500);
 | 
				
			||||||
 | 
					        --hover-background-color:var(--fw-blue-600);
 | 
				
			||||||
 | 
					        --active-background-color:var(--fw-blue-700);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .is-destructive {
 | 
					    .secondary, button {
 | 
				
			||||||
      --fw-bg-color: var(--fw-red-400);
 | 
					      --color: var(--fw-gray-700);
 | 
				
			||||||
      --fw-text-color: var(--fw-red-010);
 | 
					      --background-color: var(--fw-gray-200);
 | 
				
			||||||
 | 
					      --border-color:var(--fw-gray-700);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &.is-colored {
 | 
					      --hover-color:var(--fw-gray-800);
 | 
				
			||||||
        &[disabled] {
 | 
					      --hover-background-color:var(--fw-gray-300);
 | 
				
			||||||
          --fw-bg-color: var(--fw-red-100) !important;
 | 
					      --hover-border-color:var(--fw-gray-800);
 | 
				
			||||||
          --fw-text-color: var(--fw-blue-900) !important;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.is-hovered,
 | 
					      --active-color:var(--fw-gray-970);
 | 
				
			||||||
        &:hover {
 | 
					      --active-background-color:var(--fw-gray-400);
 | 
				
			||||||
          --fw-bg-color: var(--fw-red-600);
 | 
					      --active-border-color:var(--fw-gray-400);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.is-active,
 | 
					      --pressed-color:var(--fw-beige-200);
 | 
				
			||||||
        &.active,
 | 
					      --pressed-background-color:var(--fw-gray-900);
 | 
				
			||||||
        &:active {
 | 
					
 | 
				
			||||||
          --fw-bg-color: var(--fw-red-700);
 | 
					      --disabled-color:var(--fw-gray-500);
 | 
				
			||||||
          &.router-link-exact-active {
 | 
					      --disabled-background-color:var(--fw-gray-100);
 | 
				
			||||||
            --fw-bg-color: var(--fw-red-800);
 | 
					      --disabled-border-color:var(--fw-gray-100);
 | 
				
			||||||
          }
 | 
					
 | 
				
			||||||
        }
 | 
					      &.raised {
 | 
				
			||||||
 | 
					        --background-color:var(--fw-gray-300);
 | 
				
			||||||
 | 
					        --border-color:var(--fw-gray-300);
 | 
				
			||||||
 | 
					        --hover-background-color:var(--fw-gray-400);
 | 
				
			||||||
 | 
					        --active-background-color:var(--fw-gray-500);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .destructive {
 | 
				
			||||||
 | 
					      --color: var(--fw-red-010);
 | 
				
			||||||
 | 
					      --background-color: var(--fw-red-500);
 | 
				
			||||||
 | 
					      --border-color:var(--fw-red-500);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      --hover-color:var(--fw-gray-100);
 | 
				
			||||||
 | 
					      --hover-background-color:var(--fw-red-600);
 | 
				
			||||||
 | 
					      --hover-border-color:var(--fw-red-600);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      --active-color:var(--fw-gray-100);
 | 
				
			||||||
 | 
					      --active-background-color:var(--fw-red-700);
 | 
				
			||||||
 | 
					      --active-border-color:var(--fw-red-700);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      --disabled-color:var(--fw-gray-500);
 | 
				
			||||||
 | 
					      --disabled-background-color:var(--fw-gray-100);
 | 
				
			||||||
 | 
					      --disabled-border-color:var(--fw-gray-100);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .blue {
 | 
				
			||||||
 | 
					      --color: var(--fw-blue-900);
 | 
				
			||||||
 | 
					      --background-color: var(--fw-pastel-blue-1);
 | 
				
			||||||
 | 
					      .raised, button {
 | 
				
			||||||
 | 
					        --background-color: var(--fw-pastel-blue-2);
 | 
				
			||||||
 | 
					        --hover-background-color: var(--fw-pastel-blue-3);
 | 
				
			||||||
 | 
					        --active-background-color: var(--fw-pastel-blue-4);
 | 
				
			||||||
 | 
					        --disabled-color: var(--fw-gray-700);
 | 
				
			||||||
 | 
					        --disabled-border-color: var(--fw-gray-400);
 | 
				
			||||||
 | 
					        --disabled-background-color: transparent;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .red {
 | 
				
			||||||
 | 
					      --color: var(--fw-red-900);
 | 
				
			||||||
 | 
					      --background-color: var(--fw-pastel-red-2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .purple {
 | 
				
			||||||
 | 
					      --color: var(--fw-gray-970);
 | 
				
			||||||
 | 
					      --background-color: var(--fw-pastel-purple-1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .green {
 | 
				
			||||||
 | 
					      --color: var(--fw-gray-900);
 | 
				
			||||||
 | 
					      --background-color: var(--fw-pastel-green-1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .yellow {
 | 
				
			||||||
 | 
					      --color: var(--fw-gray-900);
 | 
				
			||||||
 | 
					      --background-color: var(--fw-pastel-yellow-1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @include dark-theme {
 | 
					  @include dark-theme {
 | 
				
			||||||
| 
						 | 
					@ -209,7 +341,7 @@
 | 
				
			||||||
      &.is-colored {
 | 
					      &.is-colored {
 | 
				
			||||||
        &.is-hovered,
 | 
					        &.is-hovered,
 | 
				
			||||||
        &:hover {
 | 
					        &:hover {
 | 
				
			||||||
          --fw-bg-color: var(--fw-gray-800);
 | 
					          --fw-bg-color: var(--fw-gray-950);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.is-active,
 | 
					        &.is-active,
 | 
				
			||||||
| 
						 | 
					@ -217,7 +349,7 @@
 | 
				
			||||||
        &:active {
 | 
					        &:active {
 | 
				
			||||||
          --fw-bg-color: var(--fw-gray-900);
 | 
					          --fw-bg-color: var(--fw-gray-900);
 | 
				
			||||||
          &.router-link-exact-active {
 | 
					          &.router-link-exact-active {
 | 
				
			||||||
            --fw-bg-color: var(--fw-gray-950);
 | 
					            --fw-bg-color: var(--fw-gray-850);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -252,6 +384,7 @@
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.funkwhale {
 | 
					.funkwhale {
 | 
				
			||||||
  @each $pastel in ("blue", "red", "green", "purple", "yellow") {
 | 
					  @each $pastel in ("blue", "red", "green", "purple", "yellow") {
 | 
				
			||||||
    &.is-#{$pastel} {
 | 
					    &.is-#{$pastel} {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import { ref, onMounted } from 'vue'
 | 
				
			||||||
import { useUploadsStore } from '../stores/upload'
 | 
					import { useUploadsStore } from '../stores/upload'
 | 
				
			||||||
import { useI18n } from 'vue-i18n'
 | 
					import { useI18n } from 'vue-i18n'
 | 
				
			||||||
import { useStore } from '~/store'
 | 
					import { useStore } from '~/store'
 | 
				
			||||||
 | 
					import { color } from '~/composables/colors'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Input from '~/components/ui/Input.vue'
 | 
					import Input from '~/components/ui/Input.vue'
 | 
				
			||||||
import Link from '~/components/ui/Link.vue'
 | 
					import Link from '~/components/ui/Link.vue'
 | 
				
			||||||
| 
						 | 
					@ -23,7 +24,7 @@ const uploads = useUploadsStore()
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <aside :class="[$style.sidebar, $style['sticky-content']]">
 | 
					  <aside :class="[$style.sidebar, $style['sticky-content']]" v-bind="color('default solid raised')">
 | 
				
			||||||
      <nav :class="$style['quick-actions']">
 | 
					      <nav :class="$style['quick-actions']">
 | 
				
			||||||
        <Link to="/">
 | 
					        <Link to="/">
 | 
				
			||||||
          <img
 | 
					          <img
 | 
				
			||||||
| 
						 | 
					@ -135,8 +136,6 @@ const uploads = useUploadsStore()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style module lang="scss">
 | 
					<style module lang="scss">
 | 
				
			||||||
.sidebar {
 | 
					.sidebar {
 | 
				
			||||||
  background-color: var(--fw-bg-raised)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  height: 100%;
 | 
					  height: 100%;
 | 
				
			||||||
  display:flex;
 | 
					  display:flex;
 | 
				
			||||||
  flex-direction:column;
 | 
					  flex-direction:column;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ const currentFilter = ref(filterItems[0])
 | 
				
			||||||
    title="Upload music to library"
 | 
					    title="Upload music to library"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <template #alert="closeAlert">
 | 
					    <template #alert="closeAlert">
 | 
				
			||||||
      <Alert>
 | 
					      <Alert yellow>
 | 
				
			||||||
        Before uploading, please ensure your files are tagged properly.
 | 
					        Before uploading, please ensure your files are tagged properly.
 | 
				
			||||||
        We recommend using Picard for that purpose.
 | 
					        We recommend using Picard for that purpose.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,91 +13,83 @@ import Button from "~/components/ui/Button.vue"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Funkwhale alerts support a range of pastel colors for visual appeal.
 | 
					Funkwhale alerts support a range of pastel colors for visual appeal.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::: details Colors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- Red
 | 
					 | 
				
			||||||
- Blue
 | 
					 | 
				
			||||||
- Purple
 | 
					 | 
				
			||||||
- Green
 | 
					 | 
				
			||||||
- Yellow
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Blue
 | 
					### Blue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Alert color="blue">
 | 
					<Alert blue>
 | 
				
			||||||
  Blue alert
 | 
					  Blue alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Alert color="blue">
 | 
					<Alert blue>
 | 
				
			||||||
  Blue alert
 | 
					  Blue alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Red
 | 
					### Red
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Alert color="red">
 | 
					<Alert red>
 | 
				
			||||||
  Red alert
 | 
					  Red alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Alert color="red">
 | 
					<Alert red>
 | 
				
			||||||
  Red alert
 | 
					  Red alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Purple
 | 
					### Purple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Alert color="purple">
 | 
					<Alert purple>
 | 
				
			||||||
  Purple alert
 | 
					  Purple alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Alert color="purple">
 | 
					<Alert purple>
 | 
				
			||||||
  Purple alert
 | 
					  Purple burglar alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Green
 | 
					### Green
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Alert color="green">
 | 
					<Alert green>
 | 
				
			||||||
  Green alert
 | 
					  Green alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Alert color="green">
 | 
					<Alert green>
 | 
				
			||||||
  Green alert
 | 
					  Green alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Yellow
 | 
					### Yellow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Alert color="yellow">
 | 
					<Alert yellow>
 | 
				
			||||||
  Yellow alert
 | 
					  Yellow alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Alert color="yellow">
 | 
					<Alert yellow>
 | 
				
			||||||
  Yellow alert
 | 
					  Yellow alert
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Alert actions
 | 
					## Alert actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html{2-4}
 | 
					```vue-html{2-4}
 | 
				
			||||||
<Alert>
 | 
					<Alert blue>
 | 
				
			||||||
  Awesome artist
 | 
					  Awesome artist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <template #actions>
 | 
					  <template #actions>
 | 
				
			||||||
 | 
					    <Button disabled>Deny</Button>
 | 
				
			||||||
    <Button>Got it</Button>
 | 
					    <Button>Got it</Button>
 | 
				
			||||||
  </template>
 | 
					  </template>
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Alert>
 | 
					<Alert blue>
 | 
				
			||||||
  Awesome artist
 | 
					  Awesome artist
 | 
				
			||||||
  <template #actions>
 | 
					  <template #actions>
 | 
				
			||||||
 | 
					    <Button disabled>Deny</Button>
 | 
				
			||||||
    <Button>Got it</Button>
 | 
					    <Button>Got it</Button>
 | 
				
			||||||
  </template>
 | 
					  </template>
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,15 +8,15 @@ const click = () => new Promise(resolve => setTimeout(resolve, 1000))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buttons are UI elements that users can interact with to perform actions. Funkwhale uses buttons in many contexts.
 | 
					Buttons are UI elements that users can interact with to perform actions. Funkwhale uses buttons in many contexts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Prop         | Data type                                 | Required? | Default   | Description                                                        |
 | 
					| Prop           | Data type | Required? | Default | Description                                    |
 | 
				
			||||||
| ------------ | ----------------------------------------- | --------- | --------- | ------------------------------------------------------------------ |
 | 
					| -------------- | --------- | --------- | ------- | ---------------------------------------------- |
 | 
				
			||||||
| `variant`    | `solid` \| `outline` \| `ghost`           | No        | `solid`   | Whether to render the button as an solid, outline or ghost button. |
 | 
					| `shadow`       | Boolean   | No        | `false` | Whether to render the button with a shadow     |
 | 
				
			||||||
| `shadow`     | Boolean                                   | No        | `false`   | Whether to render the button with a shadow                         |
 | 
					| `round`        | Boolean   | No        | `false` | Whether to render the button as a round button |
 | 
				
			||||||
| `round`      | Boolean                                   | No        | `false`   | Whether to render the button as a round button                     |
 | 
					| `icon`         | String    | No        |         | The icon attached to the button                |
 | 
				
			||||||
| `icon`       | String                                    | No        |           | The icon attached to the button                                    |
 | 
					| `aria-pressed` | Boolean   | No        | `false` | Whether the button is in an active state       |
 | 
				
			||||||
| `is-active`  | Boolean                                   | No        | `false`   | Whether the button is in an active state                           |
 | 
					| `is-loading`   | Boolean   | No        | `false` | Whether the button is in a loading state       |
 | 
				
			||||||
| `is-loading` | Boolean                                   | No        | `false`   | Whether the button is in a loading state                           |
 | 
					
 | 
				
			||||||
| `color`      | `primary` \| `secondary` \| `destructive` | No        | `primary` | Renders a colored button                                           |
 | 
					In addition, use [Colors] and [Variants]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Button colors
 | 
					## Button colors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,12 +31,12 @@ This is the default type. If you don't specify a type, a primary button is rende
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Button>
 | 
					<Button primary>
 | 
				
			||||||
  Primary button
 | 
					  Primary button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button>
 | 
					<Button primary>
 | 
				
			||||||
  Primary button
 | 
					  Primary button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,12 +45,12 @@ This is the default type. If you don't specify a type, a primary button is rende
 | 
				
			||||||
Secondary buttons represent **neutral** actions such as cancelling a change or dismissing a notification.
 | 
					Secondary buttons represent **neutral** actions such as cancelling a change or dismissing a notification.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Button color="secondary">
 | 
					<Button secondary>
 | 
				
			||||||
  Secondary button
 | 
					  Secondary button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button color="secondary">
 | 
					<Button secondary>
 | 
				
			||||||
  Secondary button
 | 
					  Secondary button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,12 +59,12 @@ Secondary buttons represent **neutral** actions such as cancelling a change or d
 | 
				
			||||||
Desctrutive buttons represent **dangerous** actions including deleting items or purging domain information.
 | 
					Desctrutive buttons represent **dangerous** actions including deleting items or purging domain information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Button color="destructive">
 | 
					<Button destructive>
 | 
				
			||||||
  Destructive button
 | 
					  Destructive button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button color="destructive">
 | 
					<Button destructive>
 | 
				
			||||||
  Destructive button
 | 
					  Destructive button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,23 +84,31 @@ This is the default style. If you don't specify a style, a solid button is rende
 | 
				
			||||||
<Button>
 | 
					<Button>
 | 
				
			||||||
  Filled button
 | 
					  Filled button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button solid>
 | 
				
			||||||
 | 
					  Also filled button
 | 
				
			||||||
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button>
 | 
					<Button>
 | 
				
			||||||
  Filled button
 | 
					  Filled button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button solid>
 | 
				
			||||||
 | 
					  Also filled button
 | 
				
			||||||
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Outline
 | 
					### Outline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Outline buttons have a transparent background. Use these to deemphasize the action the button performs.
 | 
					Outline buttons have a transparent background. Use these to deemphasize the action the button performs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Button variant="outline" color="secondary">
 | 
					<Button outline secondary>
 | 
				
			||||||
  Outline button
 | 
					  Outline button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button variant="outline" color="secondary">
 | 
					<Button outline secondary>
 | 
				
			||||||
  Outline button
 | 
					  Outline button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,12 +117,12 @@ Outline buttons have a transparent background. Use these to deemphasize the acti
 | 
				
			||||||
Ghost buttons have a transparent background and border. Use these to deemphasize the action the button performs.
 | 
					Ghost buttons have a transparent background and border. Use these to deemphasize the action the button performs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Button variant="ghost" color="secondary">
 | 
					<Button ghost secondary>
 | 
				
			||||||
  Ghost button
 | 
					  Ghost button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button variant="ghost" color="secondary">
 | 
					<Button ghost secondary>
 | 
				
			||||||
  Ghost button
 | 
					  Ghost button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -176,15 +184,35 @@ You can pass a state to indicate whether a user can interact with a button.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Active
 | 
					### Active
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A button is active when clicked by a user. You can force an active state by passing an `is-active` prop.
 | 
					You can force an active state by passing an `aria-pressed` prop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This can be useful for toggle buttons (if you don't want to use a [Toggle component](toggle))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Button is-active>
 | 
					<Button aria-pressed>
 | 
				
			||||||
  Active button
 | 
					  Active button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button is-active>
 | 
					**Secondary (default):**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button>
 | 
				
			||||||
 | 
					  Inactive button
 | 
				
			||||||
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button aria-pressed>
 | 
				
			||||||
 | 
					  Active button
 | 
				
			||||||
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Primary:**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button primary>
 | 
				
			||||||
 | 
					  Inactive button
 | 
				
			||||||
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button primary aria-pressed>
 | 
				
			||||||
  Active button
 | 
					  Active button
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -270,9 +298,9 @@ Icon buttons shrink down to the icon size if you don't pass any content. If you
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button color="secondary" icon="bi-three-dots-vertical" />
 | 
					<Button icon="bi-three-dots-vertical" />
 | 
				
			||||||
<Button color="secondary" round icon="bi-x" />
 | 
					<Button round icon="bi-x" />
 | 
				
			||||||
<Button icon="bi-save"> </Button>
 | 
					<Button primary icon="bi-save"> </Button>
 | 
				
			||||||
<Button color="destructive" icon="bi-trash">
 | 
					<Button destructive icon="bi-trash">
 | 
				
			||||||
  Delete
 | 
					  Delete
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,14 +25,14 @@ Add a 16px gap between adjacent items.
 | 
				
			||||||
<Layout flex>
 | 
					<Layout flex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Alert color="green">A</Alert>
 | 
					<Alert green">A</Alert>
 | 
				
			||||||
<Alert color="red">B</Alert>
 | 
					<Alert red">B</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="preview">
 | 
					<div class="preview">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Alert color="green">A</Alert>
 | 
					<Alert green>A</Alert>
 | 
				
			||||||
<Alert color="red">B</Alert>
 | 
					<Alert purple>B</Alert>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,15 +43,15 @@ Add a 16px gap between adjacent items.
 | 
				
			||||||
<Layout flex>
 | 
					<Layout flex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html{2}
 | 
					```vue-html{2}
 | 
				
			||||||
<Alert color="green">A</Alert>
 | 
					<Alert green">A</Alert>
 | 
				
			||||||
<Spacer/>
 | 
					<Spacer/>
 | 
				
			||||||
<Alert color="red">B</Alert>
 | 
					<Alert red">B</Alert>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="preview">
 | 
					<div class="preview">
 | 
				
			||||||
<Alert color="green">A</Alert>
 | 
					<Alert green>A</Alert>
 | 
				
			||||||
<Spacer/>
 | 
					<Spacer/>
 | 
				
			||||||
<Alert color="red">B</Alert>
 | 
					<Alert purple>B</Alert>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
| 
						 | 
					@ -62,19 +62,19 @@ Add a 16px gap between adjacent items.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html{4}
 | 
					```vue-html{4}
 | 
				
			||||||
<Layout flex>
 | 
					<Layout flex>
 | 
				
			||||||
  <Alert color="blue">A</Alert>
 | 
					  <Alert blue">A</Alert>
 | 
				
			||||||
  <Alert color="green">A</Alert>
 | 
					  <Alert yellow">B</Alert>
 | 
				
			||||||
  <Spacer/>
 | 
					  <Spacer/>
 | 
				
			||||||
  <Alert color="red">B</Alert>
 | 
					  <Alert red">C</Alert>
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="preview">
 | 
					<div class="preview">
 | 
				
			||||||
<Layout flex>
 | 
					<Layout flex>
 | 
				
			||||||
<Alert color="blue">A</Alert>
 | 
					<Alert blue>A</Alert>
 | 
				
			||||||
<Alert color="green">A</Alert>
 | 
					<Alert yellow>B</Alert>
 | 
				
			||||||
<Spacer/>
 | 
					<Spacer/>
 | 
				
			||||||
<Alert color="red">B</Alert>
 | 
					<Alert red>C</Alert>
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
| 
						 | 
					@ -91,10 +91,9 @@ const size = ref(1);
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Input v-model="size" type="range" />
 | 
					  <Input v-model="size" type="range" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <Alert color="blue">A</Alert>
 | 
					  <Alert yellow>A</Alert>
 | 
				
			||||||
  <Alert color="green">A</Alert>
 | 
					 | 
				
			||||||
  <Spacer :size="size" />
 | 
					  <Spacer :size="size" />
 | 
				
			||||||
  <Alert color="red">B</Alert>
 | 
					  <Alert purple>B</Alert>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,9 +102,9 @@ const size = ref(1);
 | 
				
			||||||
  {{ size }}px
 | 
					  {{ size }}px
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<div class="preview">
 | 
					<div class="preview">
 | 
				
			||||||
  <Alert color="blue">A</Alert>
 | 
					  <Alert yellow>A</Alert>
 | 
				
			||||||
  <Spacer :size="size" />
 | 
					  <Spacer :size="size" />
 | 
				
			||||||
  <Alert color="red">B</Alert>
 | 
					  <Alert purple>B</Alert>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,14 +122,14 @@ const size = ref(1);
 | 
				
			||||||
  <Layout stack no-gap
 | 
					  <Layout stack no-gap
 | 
				
			||||||
    :style="{ height: size + '%' }"
 | 
					    :style="{ height: size + '%' }"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <Alert>A</Alert>
 | 
					    <Alert blue>A</Alert>
 | 
				
			||||||
    <Spacer grow title="grow" />
 | 
					    <Spacer grow title="grow" />
 | 
				
			||||||
    <Alert>B</Alert>
 | 
					    <Alert red>B</Alert>
 | 
				
			||||||
    <Alert>C</Alert>
 | 
					    <Alert green>C</Alert>
 | 
				
			||||||
    <Spacer shrink title="shrink" />
 | 
					    <Spacer shrink title="shrink" />
 | 
				
			||||||
    <Alert>D</Alert>
 | 
					    <Alert purple>D</Alert>
 | 
				
			||||||
    <Spacer grow shrink title="grow shrink" />
 | 
					    <Spacer grow shrink title="grow shrink" />
 | 
				
			||||||
    <Alert>E</Alert>
 | 
					    <Alert yellow>E</Alert>
 | 
				
			||||||
  </Layout>
 | 
					  </Layout>
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
| 
						 | 
					@ -141,14 +140,14 @@ const size = ref(1);
 | 
				
			||||||
<Input v-model="size" type="range" style="writing-mode: vertical-lr; height:100%"><template #input-right>{{ size }}%</template></Input>
 | 
					<Input v-model="size" type="range" style="writing-mode: vertical-lr; height:100%"><template #input-right>{{ size }}%</template></Input>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Layout stack no-gap :style="{ height: size + '%'}">
 | 
					<Layout stack no-gap :style="{ height: size + '%'}">
 | 
				
			||||||
  <Alert>A</Alert>
 | 
					  <Alert blue>A</Alert>
 | 
				
			||||||
  <Spacer grow title="grow" />
 | 
					  <Spacer grow title="grow" />
 | 
				
			||||||
  <Alert>B</Alert>
 | 
					  <Alert red>B</Alert>
 | 
				
			||||||
  <Alert>C</Alert>
 | 
					  <Alert green>C</Alert>
 | 
				
			||||||
  <Spacer shrink title="shrink" />
 | 
					  <Spacer shrink title="shrink" />
 | 
				
			||||||
  <Alert>D</Alert>
 | 
					  <Alert purple>D</Alert>
 | 
				
			||||||
  <Spacer grow shrink title="grow shrink" />
 | 
					  <Spacer grow shrink title="grow shrink" />
 | 
				
			||||||
  <Alert>E</Alert>
 | 
					  <Alert yellow>E</Alert>
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Layout>
 | 
					</Layout>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,7 +70,7 @@ Make sure to add `autofocus` to the preferred button.
 | 
				
			||||||
  Modal content
 | 
					  Modal content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <template #actions>
 | 
					  <template #actions>
 | 
				
			||||||
    <Button @click="isOpen = false" color="secondary">
 | 
					    <Button @click="isOpen = false">
 | 
				
			||||||
      Cancel
 | 
					      Cancel
 | 
				
			||||||
    </Button>
 | 
					    </Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,7 +88,7 @@ Make sure to add `autofocus` to the preferred button.
 | 
				
			||||||
<Modal v-model="isOpen2" title="My modal">
 | 
					<Modal v-model="isOpen2" title="My modal">
 | 
				
			||||||
  Modal content
 | 
					  Modal content
 | 
				
			||||||
  <template #actions>
 | 
					  <template #actions>
 | 
				
			||||||
    <Button @click="isOpen2 = false" color="secondary">
 | 
					    <Button @click="isOpen2 = false">
 | 
				
			||||||
      Cancel
 | 
					      Cancel
 | 
				
			||||||
    </Button>
 | 
					    </Button>
 | 
				
			||||||
    <Button autofocus @click="isOpen2 = false">
 | 
					    <Button autofocus @click="isOpen2 = false">
 | 
				
			||||||
| 
						 | 
					@ -120,13 +120,13 @@ Note that confirmation dialogs interrupt the user's workflow. Consider adding a
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Button @click="isOpen = true" color="destructive">
 | 
					<Button @click="isOpen = true" destructive>
 | 
				
			||||||
  Delete my account ...
 | 
					  Delete my account ...
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Modal v-model="isOpen" title="Delete account?">
 | 
					<Modal v-model="isOpen" title="Delete account?">
 | 
				
			||||||
  <template #alert>
 | 
					  <template #alert>
 | 
				
			||||||
    <Alert color="red">
 | 
					    <Alert red>
 | 
				
			||||||
1 082 music files that you uploaded will be deleted.<br />
 | 
					1 082 music files that you uploaded will be deleted.<br />
 | 
				
			||||||
7 879 items in your collections will be unlinked.
 | 
					7 879 items in your collections will be unlinked.
 | 
				
			||||||
    </Alert>
 | 
					    </Alert>
 | 
				
			||||||
| 
						 | 
					@ -135,22 +135,22 @@ Do you want to delete your account forever?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You will not be able to restore your account.
 | 
					You will not be able to restore your account.
 | 
				
			||||||
  <template #actions>
 | 
					  <template #actions>
 | 
				
			||||||
    <Button autofocus @click="isOpen = false" color="secondary">
 | 
					    <Button autofocus @click="isOpen = false" >
 | 
				
			||||||
      Keep my account
 | 
					      Keep my account
 | 
				
			||||||
    </Button>
 | 
					    </Button>
 | 
				
			||||||
    <Button color="destructive" @click="isOpen = false">
 | 
					    <Button destructive @click="isOpen = false">
 | 
				
			||||||
      I understand. Delete my account now!
 | 
					      I understand. Delete my account now!
 | 
				
			||||||
    </Button>
 | 
					    </Button>
 | 
				
			||||||
  </template>
 | 
					  </template>
 | 
				
			||||||
</Modal>
 | 
					</Modal>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Button @click="isOpen6 = true" color="destructive">
 | 
					<Button @click="isOpen6 = true" destructive>
 | 
				
			||||||
Delete my account ...
 | 
					Delete my account ...
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
<Modal v-model="isOpen6" title="Delete account?">
 | 
					<Modal v-model="isOpen6" title="Delete account?">
 | 
				
			||||||
<template #alert>
 | 
					<template #alert>
 | 
				
			||||||
<Alert color="red">
 | 
					<Alert red>
 | 
				
			||||||
1 082 music files that you uploaded will be deleted.<br />
 | 
					1 082 music files that you uploaded will be deleted.<br />
 | 
				
			||||||
7 879 items in your collections will be unlinked.
 | 
					7 879 items in your collections will be unlinked.
 | 
				
			||||||
</Alert>
 | 
					</Alert>
 | 
				
			||||||
| 
						 | 
					@ -159,10 +159,10 @@ Do you want to delete your account forever?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You will not be able to restore your account.
 | 
					You will not be able to restore your account.
 | 
				
			||||||
<template #actions>
 | 
					<template #actions>
 | 
				
			||||||
<Button autofocus @click="isOpen6 = false" color="secondary">
 | 
					<Button autofocus @click="isOpen6 = false">
 | 
				
			||||||
Keep my account
 | 
					Keep my account
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
<Button color="destructive" @click="isOpen6 = false">
 | 
					<Button destructive @click="isOpen6 = false">
 | 
				
			||||||
I understand. Delete my account now!
 | 
					I understand. Delete my account now!
 | 
				
			||||||
</Button>
 | 
					</Button>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -239,7 +239,7 @@ You can nest [Funkwhale alerts](./alert) to visually highlight content within th
 | 
				
			||||||
<Modal v-model="isOpen3" title="My modal">
 | 
					<Modal v-model="isOpen3" title="My modal">
 | 
				
			||||||
  Modal content
 | 
					  Modal content
 | 
				
			||||||
  <template #alert v-if="alertOpen">
 | 
					  <template #alert v-if="alertOpen">
 | 
				
			||||||
    <Alert>
 | 
					    <Alert blue>
 | 
				
			||||||
      Alert content
 | 
					      Alert content
 | 
				
			||||||
      <template #actions>
 | 
					      <template #actions>
 | 
				
			||||||
        <Button autofocus @click="alertOpen = false">Close alert</Button>
 | 
					        <Button autofocus @click="alertOpen = false">Close alert</Button>
 | 
				
			||||||
| 
						 | 
					@ -250,7 +250,7 @@ You can nest [Funkwhale alerts](./alert) to visually highlight content within th
 | 
				
			||||||
    <Button @click="isOpen3 = false" color="secondary">
 | 
					    <Button @click="isOpen3 = false" color="secondary">
 | 
				
			||||||
      Cancel
 | 
					      Cancel
 | 
				
			||||||
    </Button>
 | 
					    </Button>
 | 
				
			||||||
    <Button @click="isOpen3 = false">
 | 
					    <Button primary @click="isOpen3 = false">
 | 
				
			||||||
      Ok
 | 
					      Ok
 | 
				
			||||||
    </Button>
 | 
					    </Button>
 | 
				
			||||||
  </template>
 | 
					  </template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,29 +12,17 @@ You can add text to pills by adding it between the `<Pill>` tags.
 | 
				
			||||||
| ------- | ----------------------------------------------------------------------------------------------- | --------- | ----------- | ---------------------- |
 | 
					| ------- | ----------------------------------------------------------------------------------------------- | --------- | ----------- | ---------------------- |
 | 
				
			||||||
| `color` | `primary` \| `secondary` \| `destructive` \| `blue` \| `red` \| `purple` \| `green` \| `yellow` | No        | `secondary` | Renders a colored pill |
 | 
					| `color` | `primary` \| `secondary` \| `destructive` \| `blue` \| `red` \| `purple` \| `green` \| `yellow` | No        | `secondary` | Renders a colored pill |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Pill types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You can assign a type to your pill to indicate what kind of information it conveys.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
::: details Types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- Primary
 | 
					 | 
				
			||||||
- Secondary
 | 
					 | 
				
			||||||
- Destructive
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Primary
 | 
					### Primary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Primary pills convey **positive** information.
 | 
					Primary pills convey **positive** information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Pill color="primary">
 | 
					<Pill primary>
 | 
				
			||||||
  Primary pill
 | 
					  Primary pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Pill color="primary">
 | 
					<Pill primary>
 | 
				
			||||||
  Primary pill
 | 
					  Primary pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,12 +49,12 @@ This is the default type for pills. If you don't specify a type, a **secondary**
 | 
				
			||||||
Destructive pills convey **destructive** or **negative** information. Use these to indicate that information could cause issues such as data loss.
 | 
					Destructive pills convey **destructive** or **negative** information. Use these to indicate that information could cause issues such as data loss.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Pill color="destructive">
 | 
					<Pill destructive>
 | 
				
			||||||
  Destructive pill
 | 
					  Destructive pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Pill color="destructive">
 | 
					<Pill destructive>
 | 
				
			||||||
  Destructive pill
 | 
					  Destructive pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,73 +62,63 @@ Destructive pills convey **destructive** or **negative** information. Use these
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Funkwhale pills support a range of pastel colors to create visually appealing interfaces.
 | 
					Funkwhale pills support a range of pastel colors to create visually appealing interfaces.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::: details Colors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- Red
 | 
					 | 
				
			||||||
- Blue
 | 
					 | 
				
			||||||
- Purple
 | 
					 | 
				
			||||||
- Green
 | 
					 | 
				
			||||||
- Yellow
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Blue
 | 
					### Blue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Pill color="blue">
 | 
					<Pill blue>
 | 
				
			||||||
  Blue pill
 | 
					  Blue pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Pill color="blue">
 | 
					<Pill blue>
 | 
				
			||||||
  Blue pill
 | 
					  Blue pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Red
 | 
					### Red
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Pill color="red">
 | 
					<Pill red>
 | 
				
			||||||
  Red pill
 | 
					  Red pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Pill color="red">
 | 
					<Pill red>
 | 
				
			||||||
  Red pill
 | 
					  Red pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Purple
 | 
					### Purple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Pill color="purple">
 | 
					<Pill purple>
 | 
				
			||||||
  Purple pill
 | 
					  Purple pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Pill color="purple">
 | 
					<Pill purple>
 | 
				
			||||||
  Purple pill
 | 
					  Purple pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Green
 | 
					### Green
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Pill color="green">
 | 
					<Pill green>
 | 
				
			||||||
  Green pill
 | 
					  Green pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Pill color="green">
 | 
					<Pill green>
 | 
				
			||||||
  Green pill
 | 
					  Green pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Yellow
 | 
					### Yellow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```vue-html
 | 
					```vue-html
 | 
				
			||||||
<Pill color="yellow">
 | 
					<Pill yellow>
 | 
				
			||||||
  Yellow pill
 | 
					  Yellow pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Pill color="yellow">
 | 
					<Pill yellow>
 | 
				
			||||||
  Yellow pill
 | 
					  Yellow pill
 | 
				
			||||||
</Pill>
 | 
					</Pill>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1,292 @@
 | 
				
			||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { color } from "~/composables/colors.ts"
 | 
				
			||||||
 | 
					import Button from "~/components/ui/Button.vue"
 | 
				
			||||||
 | 
					import Card from "~/components/ui/Card.vue"
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Using Color
 | 
					# Using Color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Add color via props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Alerts](components/ui/alert) support [Pastel](#pastel) attributes. [Buttons](components/ui/button) accept [Color](#color) and [Variant](#variant) attributes. [Cards](components/ui/card) accept [Pastel](#pastel), [Variant](#variant), [Neutral](#neutral) and [Raised](#raised) attributes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```vue-html
 | 
				
			||||||
 | 
					<Card title="solid red" solid red />
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Card title="solid red" solid red />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Add color to a any component or Html tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Choose a
 | 
				
			||||||
 | 
					   - [base color](#colors) (`primary | secondary | destructive`) or
 | 
				
			||||||
 | 
					   - [pastel color](#pastel) (`blue | red | green | yellow`) or
 | 
				
			||||||
 | 
					   - [neutral beige or gray](#neutral) (`default`) for surfaces
 | 
				
			||||||
 | 
					2. Choose a [variant](#color-variants) (`solid | ghost | outline`)
 | 
				
			||||||
 | 
					3. Add [interactivity and raise the surface](#interactive-andor-raised)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```vue
 | 
				
			||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { color } from "~/composables/colors.ts";
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div v-bind="color('primary solid interactive raised')" />
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('primary solid interactive raised')" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Base colors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Neutral
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('default solid')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('default solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('default solid raised')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('default solid interactive')" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Primary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('primary solid')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('primary solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('primary solid raised')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('primary solid interactive')" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Secondary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('secondary solid')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('secondary solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('secondary solid raised')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('secondary solid interactive')" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Destructive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('destructive solid')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('destructive solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('destructive solid raised')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('destructive solid interactive')" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Pastel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Blue, Red, Purple, Green, Yellow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('blue solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('red solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('purple solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('green solid interactive')" />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('yellow solid interactive')" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Variant
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Solid (default), Ghost, Outline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button round shadow icon="bi-x" solid />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('solid raised')">
 | 
				
			||||||
 | 
					<Button round icon="bi-x" ghost />
 | 
				
			||||||
 | 
					<Button round icon="bi-x" outline />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Button round shadow icon="bi-x" primary solid />
 | 
				
			||||||
 | 
					<div :class="$style.swatch" v-bind="color('primary solid')">
 | 
				
			||||||
 | 
					<Button round icon="bi-x" primary ghost />
 | 
				
			||||||
 | 
					<Button round icon="bi-x" primary outline />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Interactive and/or Raised
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div v-bind="color('default solid')" style="display:inline-flex;">
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid')" />
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid interactive')" />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div v-bind="color('secondary solid')" style="display:inline-flex;">
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid')" />
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid interactive')" />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div v-bind="color('primary solid')" style="display:inline-flex;">
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid')" />
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid interactive')" />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					<div v-bind="color('default raised solid')" style="display:inline-flex;">
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid raised')" />
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid interactive raised')" />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div v-bind="color('secondary raised solid')" style="display:inline-flex;">
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid raised')" />
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid interactive raised')" />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div v-bind="color('primary raised solid')" style="display:inline-flex;">
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid raised')" />
 | 
				
			||||||
 | 
					    <div :class="$style.swatch" v-bind="color('solid interactive raised')" />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Palette
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The color palette consists of Blues, Reds, Grays, Beiges, as well as pastel blue/red/green/yellow.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-010)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-100)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-400)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-500)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-600)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-700)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-800)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-blue-900)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-010)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-100)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-400)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-500)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-600)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-700)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-800)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-red-900)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-100)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-200)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-300)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-400)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-500)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-600)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-700)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-800)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.tiny]" style="background:var(--fw-gray-850)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-gray-900)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.tiny]" style="background:var(--fw-gray-950)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.tiny]" style="background:var(--fw-gray-960)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.tiny]" style="background:var(--fw-gray-970)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-beige-100)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-beige-200)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-beige-300)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-beige-400)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-blue-1)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-blue-2)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-blue-3)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-blue-4)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-red-1)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-red-2)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-red-3)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-red-4)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-purple-1)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-purple-2)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-purple-3)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-purple-4)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-green-1)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-green-2)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-green-3)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-green-4)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<br/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-yellow-1)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-yellow-2)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-yellow-3)" />
 | 
				
			||||||
 | 
					<div :class="[$style.swatch, $style.small]" style="background:var(--fw-pastel-yellow-4)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In addition, we have a single shade of orange used for secondary indicators such as active tabs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div :class="$style.swatch" style="background:var(--fw-secondary)" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Theme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Many browsers automatically turn on "night mode" to lower the light emission of the screen. Users can override the browser theme in their settings menu.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In both "dark mode" and "light mode", the colors must provide adequate contrast and consistency.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Semantic colors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We use semantic color variables that can mean a different shade depending on the currently chosen theme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- primary
 | 
				
			||||||
 | 
					- secondary (default)
 | 
				
			||||||
 | 
					- destructive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Color variants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For each semantic color, we set a foreground and a background. In addition, we need to check context: A button should have a stronger shade when the mouse is hovering over it. When a surface is raised over another surface, it should have a stronger shade, too.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- ghost (default for most other things)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - text color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- outline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - border color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- solid (default for buttons)
 | 
				
			||||||
 | 
					  - bg color
 | 
				
			||||||
 | 
					  - border color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Variants can be made interactive and/or raised:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- no alteration (default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- raised
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - text color
 | 
				
			||||||
 | 
					  - border color
 | 
				
			||||||
 | 
					  - bg color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- interactive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - can be disabled
 | 
				
			||||||
 | 
					  - can be active
 | 
				
			||||||
 | 
					  - can be exact-active
 | 
				
			||||||
 | 
					  - can be hovering
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- interactive-raised
 | 
				
			||||||
 | 
					  - combine `raised` and `interactive`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style module>
 | 
				
			||||||
 | 
					    .swatch {
 | 
				
			||||||
 | 
					        border-radius: 2em;
 | 
				
			||||||
 | 
					        min-width: 3.2em;
 | 
				
			||||||
 | 
					        min-height: 3.2em;
 | 
				
			||||||
 | 
					        margin: 1ch;
 | 
				
			||||||
 | 
					        display: inline-flex;
 | 
				
			||||||
 | 
					        box-shadow: 1px 2px 7px #0003, 0px 0px 1px #0009;
 | 
				
			||||||
 | 
					        align-items:center;
 | 
				
			||||||
 | 
					        justify-items:center;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .small{
 | 
				
			||||||
 | 
					        margin: .25rem;
 | 
				
			||||||
 | 
					        min-width: 1.6em;
 | 
				
			||||||
 | 
					        min-height: 1.6em;
 | 
				
			||||||
 | 
					        box-shadow: 1px 2px 4px #0002, 0px 0px 1px #0007;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .tiny{
 | 
				
			||||||
 | 
					        margin:  .5rem .25rem;
 | 
				
			||||||
 | 
					        min-width: 1em;
 | 
				
			||||||
 | 
					        min-height: 1em;
 | 
				
			||||||
 | 
					        box-shadow: 1px 2px 4px #0002, 0px 0px 1px #0007;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,96 @@ import Button from "~/components/ui/Button.vue";
 | 
				
			||||||
<style module></style>
 | 
					<style module></style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Alert />
 | 
					  <Alert yellow />
 | 
				
			||||||
  <Button />
 | 
					  <Button />
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Limitations of component props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					While Vue can infer props based on a type, it will fail with mysterious errors if the type is an exclusive union or has union types as keys.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I hope this will be resolved soon so we can use this more elegant way of injecting non-trivial props with full autocomplete, 21st century style:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```vue
 | 
				
			||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					type A = 'either' | 'or'
 | 
				
			||||||
 | 
					type B = 'definitely'
 | 
				
			||||||
 | 
					type Props = { [k in `${A}-${B}`]?: true }
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <Component either-definitely />
 | 
				
			||||||
 | 
					  <Component or-definitely />
 | 
				
			||||||
 | 
					  <Component />
 | 
				
			||||||
 | 
					  {{ Error: <Component either /> }} {{ Error: <Component definitely /> }}
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					::: details Example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					````ts
 | 
				
			||||||
 | 
					// Color from props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SingleOrNoProp<T extends string> = RequireOneOrNone<Record<T, true>, T>;
 | 
				
			||||||
 | 
					type SingleProp<T extends string> = RequireExactlyOne<Record<T, true>, T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Props = Simplify<
 | 
				
			||||||
 | 
					  SingleProp<Color | Default | Pastel> &
 | 
				
			||||||
 | 
					    SingleOrNoProp<Variant> &
 | 
				
			||||||
 | 
					    SingleOrNoProp<"interactive"> &
 | 
				
			||||||
 | 
					    SingleOrNoProp<"raised">
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Limit the choices:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ColorProps = Simplify<
 | 
				
			||||||
 | 
					  SingleProp<Color> & SingleOrNoProp<Variant> & SingleOrNoProp<"raised">
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type PastelProps = Simplify<
 | 
				
			||||||
 | 
					  SingleProp<Pastel> & SingleOrNoProp<"raised">
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Note that as of now, Vue does not support unions of props.
 | 
				
			||||||
 | 
					// So instead, we give it a single string:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ColorProp = Simplify<`${Color}${
 | 
				
			||||||
 | 
					  | ""
 | 
				
			||||||
 | 
					  | `-${Variant}${"" | "-raised"}`}`>;
 | 
				
			||||||
 | 
					export type PastelProp = Simplify<`${Pastel}${"" | "-raised"}`>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Using like this:
 | 
				
			||||||
 | 
					//   type Props = {...} & { [k in ColorProp]? : true }
 | 
				
			||||||
 | 
					// This will also lead to runtime errors. Why?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const isColorProp = (k: string) =>
 | 
				
			||||||
 | 
					  !![...colors, ...defaults, ...pastels].find(k.startsWith);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log(true, isColorProp("primary"));
 | 
				
			||||||
 | 
					console.log(true, isColorProp("secondary"));
 | 
				
			||||||
 | 
					console.log(true, isColorProp("red"));
 | 
				
			||||||
 | 
					console.log(false, isColorProp("Jes"));
 | 
				
			||||||
 | 
					console.log(false, isColorProp("raised"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Convenience function in case you want to hand over the props in the form
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 * <Component primary solid interactive raised >...</Component>
 | 
				
			||||||
 | 
					 * ```
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param props Any superset of type `Props`
 | 
				
			||||||
 | 
					 * @returns the corresponding `class` object
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: Make sure to implement the necessary classes in `colors.scss`!
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const colorFromProps = (props: Record<string, unknown>) =>
 | 
				
			||||||
 | 
					  color(
 | 
				
			||||||
 | 
					    Object.keys(props)
 | 
				
			||||||
 | 
					      .filter(isColorProp)
 | 
				
			||||||
 | 
					      .join(" ")
 | 
				
			||||||
 | 
					      .replace("-", " ") as ColorSelector,
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					````
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:::
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10668,6 +10668,11 @@ type-detect@^4.0.0, type-detect@^4.1.0:
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c"
 | 
					  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c"
 | 
				
			||||||
  integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==
 | 
					  integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type-fest@4.30.1:
 | 
				
			||||||
 | 
					  version "4.30.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.30.1.tgz#120b9e15177310ec4e9d5d6f187d86c0f4b55e0e"
 | 
				
			||||||
 | 
					  integrity sha512-ojFL7eDMX2NF0xMbDwPZJ8sb7ckqtlAi1GsmgsFXvErT9kFTk1r0DuQKvrCh73M6D4nngeHJmvogF9OluXs7Hw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type-fest@^0.16.0:
 | 
					type-fest@^0.16.0:
 | 
				
			||||||
  version "0.16.0"
 | 
					  version "0.16.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860"
 | 
					  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue