Add dark mode with CSS custom property theme system

Define 13 semantic color tokens (surface, text, border, hover) with
light/dark values in themes.css. Register with Tailwind via @theme inline.
Migrate all 335 Vue files from hardcoded gray/white classes to semantic
tokens. Add theme toggle (sun/moon/system) in user avatar dropdown.
Replace @tailwindcss/forms with custom form reset using theme vars.
Add status badge and alert tokens for dark mode. Theme-aware chart
grid/labels, skeleton placeholders, and editor. Inline script in
<head> prevents flash of wrong theme on load.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Darko Gjorgjijoski
2026-04-04 02:05:00 +02:00
parent 7fbe3d85a3
commit 88adfe0e50
221 changed files with 1265 additions and 982 deletions

View File

@@ -5,7 +5,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="onCancel"
/>
</div>
@@ -58,7 +58,7 @@
</BaseInputGrid>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"

View File

@@ -5,7 +5,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeCategoryModal"
/>
</div>
@@ -53,7 +53,7 @@
flex
justify-end
p-4
border-t border-gray-200 border-solid
border-t border-line-default border-solid
"
>
<BaseButton

View File

@@ -6,7 +6,7 @@
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeCompanyModal"
/>
</div>
@@ -98,7 +98,7 @@
</BaseInputGrid>
</div>
<div class="z-0 flex justify-end p-4 bg-gray-50 border-t border-gray-200">
<div class="z-0 flex justify-end p-4 bg-surface-secondary border-t border-line-default">
<BaseButton
class="mr-3 text-sm"
variant="primary-outline"

View File

@@ -10,7 +10,7 @@
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeCustomerModal"
/>
</div>
@@ -131,7 +131,7 @@
<BaseTab :title="$t('customers.portal_access')">
<BaseInputGrid class="col-span-5 lg:col-span-4">
<div class="md:col-span-2">
<p class="text-sm text-gray-500">
<p class="text-sm text-muted">
{{ $t('customers.portal_access_text') }}
</p>
@@ -168,7 +168,7 @@
<template #right>
<BaseIcon
:name="isShowPassword ? 'EyeIcon' : 'EyeSlashIcon'"
class="mr-1 text-gray-500 cursor-pointer"
class="mr-1 text-muted cursor-pointer"
@click="isShowPassword = !isShowPassword"
/>
</template>
@@ -194,7 +194,7 @@
<template #right>
<BaseIcon
:name="isShowConfirmPassword ? 'EyeIcon' : 'EyeSlashIcon'"
class="mr-1 text-gray-500 cursor-pointer"
class="mr-1 text-muted cursor-pointer"
@click="isShowConfirmPassword = !isShowConfirmPassword"
/>
</template>
@@ -423,7 +423,7 @@
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3 text-sm"

View File

@@ -6,7 +6,7 @@
{{ modalStore.title }}
</h6>
<p
class="mt-2 text-sm leading-snug text-gray-500"
class="mt-2 text-sm leading-snug text-muted"
style="max-width: 680px"
>
{{
@@ -38,7 +38,7 @@
</BaseInputGroup>
</div>
<div class="z-0 flex justify-end p-4 bg-gray-50 border-t border-gray-200">
<div class="z-0 flex justify-end p-4 bg-surface-secondary border-t border-line-default">
<BaseButton
class="mr-3 text-sm"
variant="primary-outline"

View File

@@ -10,7 +10,7 @@
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeExchangeRateModal"
/>
</div>
@@ -151,7 +151,7 @@
/>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"

View File

@@ -5,7 +5,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeDiskModal"
/>
</div>
@@ -21,7 +21,7 @@
>
<template #default="slotProps">
<div
class="z-0 flex justify-end p-4 border-t border-solid border-gray-200"
class="z-0 flex justify-end p-4 border-t border-solid border-line-default"
>
<BaseButton
class="mr-3 text-sm"

View File

@@ -5,7 +5,7 @@
{{ $t('members.invite_member') }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="$emit('close')"
/>
</div>
@@ -42,7 +42,7 @@
</BaseInputGroup>
</div>
<div class="flex justify-end p-4 border-t border-gray-200">
<div class="flex justify-end p-4 border-t border-line-default">
<BaseButton
variant="primary-outline"
class="mr-3"

View File

@@ -5,7 +5,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeItemModal"
/>
</div>
@@ -90,7 +90,7 @@
</BaseInputGrid>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"

View File

@@ -8,7 +8,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeItemUnitModal"
/>
</div>
@@ -37,7 +37,7 @@
flex
justify-end
p-4
border-t border-gray-200 border-solid
border-t border-line-default border-solid
"
>
<BaseButton

View File

@@ -5,7 +5,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeTestModal"
/>
</div>
@@ -63,7 +63,7 @@
</BaseInputGrid>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
variant="primary-outline"

View File

@@ -5,7 +5,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeNoteModal"
/>
</div>
@@ -75,7 +75,7 @@ _ />
justify-end
px-4
py-4
border-t border-solid border-gray-200
border-t border-solid border-line-default
"
>
<BaseButton

View File

@@ -5,7 +5,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closePaymentModeModal"
/>
</div>
@@ -30,7 +30,7 @@
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
variant="primary-outline"

View File

@@ -6,7 +6,7 @@
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeRolesModal"
/>
</div>
@@ -36,7 +36,7 @@
text-sm
not-italic
font-medium
text-gray-800
text-heading
px-4
md:px-8
py-1.5
@@ -50,7 +50,7 @@
text-sm
not-italic
font-medium
text-gray-300
text-subtle
px-4
md:px-8
py-1.5
@@ -72,7 +72,7 @@
</div>
</div>
<div class="border-t border-gray-200 py-3">
<div class="border-t border-line-default py-3">
<div
class="
grid grid-cols-1
@@ -89,7 +89,7 @@
:key="gIndex"
class="flex flex-col space-y-1"
>
<p class="text-sm text-gray-500 border-b border-gray-200 pb-1 mb-2">
<p class="text-sm text-muted border-b border-line-default pb-1 mb-2">
{{ gIndex }}
</p>
<div
@@ -122,7 +122,7 @@
flex
justify-end
p-4
border-t border-solid border-gray-200
border-t border-solid border-line-default
"
>
<BaseButton

View File

@@ -5,7 +5,7 @@
{{ modalTitle }}
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeModal"
/>
</div>
@@ -26,7 +26,7 @@
relative
flex flex-col
m-2
border border-gray-200 border-solid
border border-line-default border-solid
cursor-pointer
hover:border-primary-300
"
@@ -46,11 +46,11 @@
/>
<span
:class="[
'w-full p-1 bg-gray-200 text-sm text-center absolute bottom-0 left-0',
'w-full p-1 bg-surface-muted text-sm text-center absolute bottom-0 left-0',
{
'text-primary-500 bg-primary-100':
selectedTemplate === template.name,
'text-gray-600': selectedTemplate != template.name,
'text-body': selectedTemplate != template.name,
},
]"
>
@@ -70,7 +70,7 @@
</div>
</div>
<div class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid">
<div class="z-0 flex justify-end p-4 border-t border-line-default border-solid">
<BaseButton class="mr-3" variant="primary-outline" @click="closeModal">
{{ $t('general.cancel') }}
</BaseButton>

View File

@@ -9,7 +9,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeSendEstimateModal"
/>
</div>
@@ -87,7 +87,7 @@
</BaseInputGrid>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"
@@ -112,7 +112,7 @@
</div>
</form>
<div v-else>
<div class="my-6 mx-4 border border-gray-200 relative">
<div class="my-6 mx-4 border border-line-default relative">
<BaseButton
class="absolute top-4 right-4"
:disabled="isLoading"
@@ -131,7 +131,7 @@
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"

View File

@@ -9,7 +9,7 @@
{{ modalTitle }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeSendInvoiceModal"
/>
</div>
@@ -90,7 +90,7 @@
</BaseInputGrid>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"
@@ -120,7 +120,7 @@
</div>
</form>
<div v-else>
<div class="my-6 mx-4 border border-gray-200 relative">
<div class="my-6 mx-4 border border-line-default relative">
<BaseButton
class="absolute top-4 right-4"
:disabled="isLoading"
@@ -139,7 +139,7 @@
></iframe>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"

View File

@@ -9,7 +9,7 @@
{{ modalTitle }}
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeSendPaymentModal"
/>
</div>
@@ -90,7 +90,7 @@
</BaseInputGrid>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"
@@ -120,7 +120,7 @@
</div>
</form>
<div v-else>
<div class="my-6 mx-4 border border-gray-200 relative">
<div class="my-6 mx-4 border border-line-default relative">
<BaseButton
class="absolute top-4 right-4"
:disabled="isLoading"
@@ -139,7 +139,7 @@
></iframe>
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3"

View File

@@ -8,7 +8,7 @@
{{ modalStore.title }}
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeTaxTypeModal"
/>
</div>
@@ -106,7 +106,7 @@
flex
justify-end
p-4
border-t border-solid border-gray-200
border-t border-solid border-line-default
"
>
<BaseButton

View File

@@ -5,13 +5,13 @@
<div class="flex flex-col">
{{ modalStore.title }}
<p class="text-sm text-gray-500 mt-1">
<p class="text-sm text-muted mt-1">
{{ modalStore.content }}
</p>
</div>
<BaseIcon
name="XMarkIcon"
class="h-6 w-6 text-gray-500 cursor-pointer"
class="h-6 w-6 text-muted cursor-pointer"
@click="closeModal"
/>
</div>
@@ -88,7 +88,7 @@
</div>
<div
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
class="z-0 flex justify-end p-4 border-t border-line-default border-solid"
>
<BaseButton
class="mr-3 text-sm"

View File

@@ -6,7 +6,7 @@
<BaseIcon
name="XMarkIcon"
class="w-6 h-6 text-gray-500 cursor-pointer"
class="w-6 h-6 text-muted cursor-pointer"
@click="closeCustomFieldModal"
/>
</div>
@@ -123,7 +123,7 @@
class="ml-1 cursor-pointer"
:class="
customFieldStore.currentCustomField.in_use
? 'text-gray-300'
? 'text-subtle'
: 'text-red-300'
"
@click="removeOption(index)"
@@ -179,7 +179,7 @@
flex
justify-end
p-4
border-t border-solid border-gray-200
border-t border-solid border-line-default
"
>
<BaseButton

View File

@@ -153,7 +153,7 @@
<BaseSwitch v-model="set_as_default" class="flex" />
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
<p class="p-0 mb-1 text-base leading-snug text-heading box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>

View File

@@ -132,7 +132,7 @@
<BaseSwitch v-model="set_as_default" class="flex" />
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
<p class="p-0 mb-1 text-base leading-snug text-heading box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>

View File

@@ -63,7 +63,7 @@
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
<p class="p-0 mb-1 text-base leading-snug text-heading box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>

View File

@@ -152,7 +152,7 @@
<BaseSwitch v-model="set_as_default" class="flex" />
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
<p class="p-0 mb-1 text-base leading-snug text-heading box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>

View File

@@ -134,7 +134,7 @@
<BaseSwitch v-model="set_as_default" class="flex" />
</div>
<div class="ml-4 right">
<p class="p-0 mb-1 text-base leading-snug text-black box-title">
<p class="p-0 mb-1 text-base leading-snug text-heading box-title">
{{ $t('settings.disk.is_default') }}
</p>
</div>