mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-16 09:44:06 +00:00
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:
@@ -6,9 +6,9 @@
|
||||
class="
|
||||
relative
|
||||
overflow-hidden
|
||||
bg-white
|
||||
border border-gray-100
|
||||
shadow-sm
|
||||
bg-surface
|
||||
border border-line-light
|
||||
shadow
|
||||
rounded-xl
|
||||
"
|
||||
>
|
||||
@@ -22,7 +22,7 @@
|
||||
:class="[
|
||||
getThClass(column),
|
||||
{
|
||||
'text-bold text-black': sort.fieldName === column.key,
|
||||
'text-bold text-heading': sort.fieldName === column.key,
|
||||
},
|
||||
]"
|
||||
@click="changeSorting(column)"
|
||||
@@ -51,7 +51,7 @@
|
||||
<tr
|
||||
v-for="placeRow in placeholderCount"
|
||||
:key="placeRow"
|
||||
:class="placeRow % 2 === 0 ? 'bg-white' : 'bg-gray-50'"
|
||||
:class="placeRow % 2 === 0 ? 'bg-surface' : 'bg-surface-secondary'"
|
||||
>
|
||||
<td
|
||||
v-for="column in columns"
|
||||
@@ -75,7 +75,7 @@
|
||||
<tr
|
||||
v-for="(row, index) in sortedRows"
|
||||
:key="row.data?.id ?? index"
|
||||
:class="index % 2 === 0 ? 'bg-white' : 'bg-gray-50'"
|
||||
:class="index % 2 === 0 ? 'bg-surface' : 'bg-surface-secondary'"
|
||||
>
|
||||
<td
|
||||
v-for="column in columns"
|
||||
@@ -114,7 +114,7 @@
|
||||
!loading && !isLoading && sortedRows && sortedRows.length === 0
|
||||
"
|
||||
class="
|
||||
text-center text-gray-500
|
||||
text-center text-muted
|
||||
pb-2
|
||||
flex
|
||||
h-[160px]
|
||||
@@ -125,7 +125,7 @@
|
||||
>
|
||||
<BaseIcon
|
||||
name="ExclamationCircleIcon"
|
||||
class="w-6 h-6 text-gray-400"
|
||||
class="w-6 h-6 text-subtle"
|
||||
/>
|
||||
|
||||
<span class="block mt-1">{{ $t('general.no_data_found') }}</span>
|
||||
@@ -163,9 +163,9 @@ const props = defineProps({
|
||||
sortOrder: { type: String, default: '' },
|
||||
tableClass: {
|
||||
type: String,
|
||||
default: 'min-w-full divide-y divide-gray-200',
|
||||
default: 'min-w-full divide-y divide-line-default',
|
||||
},
|
||||
theadClass: { type: String, default: 'bg-gray-50' },
|
||||
theadClass: { type: String, default: 'bg-surface-secondary' },
|
||||
tbodyClass: { type: String, default: '' },
|
||||
noResultsMessage: {
|
||||
type: String,
|
||||
@@ -236,7 +236,7 @@ function getColumn(columnName) {
|
||||
|
||||
function getThClass(column) {
|
||||
let classes =
|
||||
'whitespace-nowrap px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider'
|
||||
'whitespace-nowrap px-6 py-3 text-left text-xs font-medium text-muted uppercase tracking-wider'
|
||||
|
||||
if (column.defaultThClass) {
|
||||
classes = column.defaultThClass
|
||||
@@ -256,7 +256,7 @@ function getThClass(column) {
|
||||
}
|
||||
|
||||
function getTdClass(column) {
|
||||
let classes = 'px-6 py-4 text-sm text-gray-500 whitespace-nowrap'
|
||||
let classes = 'px-6 py-4 text-sm text-muted whitespace-nowrap'
|
||||
|
||||
if (column.defaultTdClass) {
|
||||
classes = column.defaultTdClass
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
justify-between
|
||||
px-4
|
||||
py-3
|
||||
bg-white
|
||||
border-t border-gray-200
|
||||
bg-surface
|
||||
border-t border-line-default
|
||||
sm:px-6
|
||||
"
|
||||
>
|
||||
@@ -16,7 +16,7 @@
|
||||
<a
|
||||
href="#"
|
||||
:class="{
|
||||
'disabled cursor-normal pointer-events-none !bg-gray-100 !text-gray-400':
|
||||
'disabled cursor-normal pointer-events-none !bg-surface-tertiary !text-subtle':
|
||||
pagination.currentPage === 1,
|
||||
}"
|
||||
class="
|
||||
@@ -27,11 +27,11 @@
|
||||
py-2
|
||||
text-sm
|
||||
font-medium
|
||||
text-gray-700
|
||||
bg-white
|
||||
border border-gray-200
|
||||
text-body
|
||||
bg-surface
|
||||
border border-line-default
|
||||
rounded-md
|
||||
hover:bg-gray-50
|
||||
hover:bg-hover
|
||||
"
|
||||
@click="pageClicked(pagination.currentPage - 1)"
|
||||
>
|
||||
@@ -40,7 +40,7 @@
|
||||
<a
|
||||
href="#"
|
||||
:class="{
|
||||
'disabled cursor-default pointer-events-none !bg-gray-100 !text-gray-400':
|
||||
'disabled cursor-default pointer-events-none !bg-surface-tertiary !text-subtle':
|
||||
pagination.currentPage === pagination.totalPages,
|
||||
}"
|
||||
class="
|
||||
@@ -52,11 +52,11 @@
|
||||
ml-3
|
||||
text-sm
|
||||
font-medium
|
||||
text-gray-700
|
||||
bg-white
|
||||
border border-gray-200
|
||||
text-body
|
||||
bg-surface
|
||||
border border-line-default
|
||||
rounded-md
|
||||
hover:bg-gray-50
|
||||
hover:bg-hover
|
||||
"
|
||||
@click="pageClicked(pagination.currentPage + 1)"
|
||||
>
|
||||
@@ -65,7 +65,7 @@
|
||||
</div>
|
||||
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-700">
|
||||
<p class="text-sm text-body">
|
||||
{{ $t('general.pagination.showing') }}
|
||||
{{ ' ' }}
|
||||
<span
|
||||
@@ -113,7 +113,7 @@
|
||||
<a
|
||||
href="#"
|
||||
:class="{
|
||||
'disabled cursor-normal pointer-events-none !bg-gray-100 !text-gray-400':
|
||||
'disabled cursor-normal pointer-events-none !bg-surface-tertiary !text-subtle':
|
||||
pagination.currentPage === 1,
|
||||
}"
|
||||
class="
|
||||
@@ -124,11 +124,11 @@
|
||||
py-2
|
||||
text-sm
|
||||
font-medium
|
||||
text-gray-500
|
||||
bg-white
|
||||
border border-gray-200
|
||||
text-muted
|
||||
bg-surface
|
||||
border border-line-default
|
||||
rounded-l-lg
|
||||
hover:bg-gray-50
|
||||
hover:bg-hover
|
||||
"
|
||||
@click="pageClicked(pagination.currentPage - 1)"
|
||||
>
|
||||
@@ -142,7 +142,7 @@
|
||||
:class="{
|
||||
'z-10 bg-primary-500 border-primary-500 text-white':
|
||||
isActive(1),
|
||||
'bg-white border-gray-200 text-gray-500 hover:bg-gray-50':
|
||||
'bg-surface border-line-default text-muted hover:bg-hover':
|
||||
!isActive(1),
|
||||
}"
|
||||
class="
|
||||
@@ -170,9 +170,9 @@
|
||||
py-2
|
||||
text-sm
|
||||
font-medium
|
||||
text-gray-700
|
||||
bg-white
|
||||
border border-gray-200
|
||||
text-body
|
||||
bg-surface
|
||||
border border-line-default
|
||||
"
|
||||
>
|
||||
...
|
||||
@@ -184,7 +184,7 @@
|
||||
:class="{
|
||||
'z-10 bg-primary-500 border-primary-500 text-white':
|
||||
isActive(page),
|
||||
'bg-white border-gray-200 text-gray-500 hover:bg-gray-50':
|
||||
'bg-surface border-line-default text-muted hover:bg-hover':
|
||||
!isActive(page),
|
||||
disabled: page === '...',
|
||||
}"
|
||||
@@ -214,9 +214,9 @@
|
||||
py-2
|
||||
text-sm
|
||||
font-medium
|
||||
text-gray-700
|
||||
bg-white
|
||||
border border-gray-200
|
||||
text-body
|
||||
bg-surface
|
||||
border border-line-default
|
||||
"
|
||||
>
|
||||
...
|
||||
@@ -228,7 +228,7 @@
|
||||
:class="{
|
||||
'z-10 bg-primary-500 border-primary-500 text-white':
|
||||
isActive(pagination.totalPages),
|
||||
'bg-white border-gray-200 text-gray-500 hover:bg-gray-50':
|
||||
'bg-surface border-line-default text-muted hover:bg-hover':
|
||||
!isActive(pagination.totalPages),
|
||||
}"
|
||||
class="
|
||||
@@ -255,14 +255,14 @@
|
||||
py-2
|
||||
text-sm
|
||||
font-medium
|
||||
text-gray-500
|
||||
bg-white
|
||||
border border-gray-200
|
||||
text-muted
|
||||
bg-surface
|
||||
border border-line-default
|
||||
rounded-r-lg
|
||||
hover:bg-gray-50
|
||||
hover:bg-hover
|
||||
"
|
||||
:class="{
|
||||
'disabled cursor-default pointer-events-none !bg-gray-100 !text-gray-400':
|
||||
'disabled cursor-default pointer-events-none !bg-surface-tertiary !text-subtle':
|
||||
pagination.currentPage === pagination.totalPages,
|
||||
}"
|
||||
@click="pageClicked(pagination.currentPage + 1)"
|
||||
|
||||
Reference in New Issue
Block a user