add outline and secondary variants to TimeTrackerStartStop button to reduce visual complexity

This commit is contained in:
Gregor Vostrak
2026-01-15 18:44:42 +01:00
parent 79999fde28
commit 3fb75ec3d5
6 changed files with 63 additions and 31 deletions

View File

@@ -53,7 +53,7 @@ test('test that starting and stopping an empty time entry shows a new time entry
// Test that description update works
async function assertThatTimeEntryRowIsStopped(newTimeEntry: Locator) {
await expect(newTimeEntry.getByTestId('timer_button')).toHaveClass(/bg-accent-300\/70/);
await expect(newTimeEntry.getByTestId('timer_button')).toHaveClass(/bg-tertiary/);
}
test('test that updating a description of a time entry in the overview works on blur', async ({
@@ -224,7 +224,7 @@ test('test that starting a time entry from the overview works', async ({ page })
const newTimeEntry = timeEntryRows.first();
const startButton = newTimeEntry.getByTestId('timer_button');
await expect(startButton).toHaveClass(/bg-accent-300\/70/);
await expect(startButton).toHaveClass(/bg-tertiary/);
await Promise.all([
page.waitForResponse(async (response) => {

View File

@@ -51,6 +51,7 @@ const isRunningInDifferentOrganization = computed(() => {
<TimeTrackerStartStop
:active="isActive"
size="base"
variant="outline"
@changed="setActiveState"></TimeTrackerStartStop>
</div>
</template>

View File

@@ -66,7 +66,9 @@ async function startTaskTimer() {
</ProjectBadge>
</div>
<div class="flex items-center justify-center">
<TimeTrackerStartStop @changed="startTaskTimer"></TimeTrackerStartStop>
<TimeTrackerStartStop
variant="outline"
@changed="startTaskTimer"></TimeTrackerStartStop>
</div>
</div>
</template>

View File

@@ -163,7 +163,8 @@ async function handleDeleteTimeEntry() {
@changed="updateStartEndTime"></TimeEntryRowDurationInput>
<TimeTrackerStartStop
:active="!!(timeEntry.start && !timeEntry.end)"
class="opacity-20 flex focus-visible:opacity-100 group-hover:opacity-100"
variant="secondary"
class="opacity-60 flex focus-visible:opacity-100 group-hover:opacity-100"
@changed="onStartStopClick"></TimeTrackerStartStop>
<TimeEntryMoreOptionsDropdown
@edit="handleEdit"

View File

@@ -1,38 +1,72 @@
<script setup lang="ts">
import { twMerge } from 'tailwind-merge';
import { computed } from 'vue';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from './utils/cn';
const timeTrackerVariants = cva(
'flex items-center justify-center transition focus:outline-0 rounded-full',
{
variants: {
variant: {
primary:
'text-white ring-accent-200/10 focus-visible:ring-ring focus-visible:ring-2 ring-4 sm:ring-[6px]',
secondary:
'bg-tertiary text-text-tertiary hover:text-text-primary focus:ring-2 focus:ring-border-tertiary',
outline:
'border border-border-primary text-text-tertiary hover:text-text-primary hover:bg-tertiary/50',
},
size: {
small: 'w-6 h-6',
base: 'w-9 h-9 hover:scale-110',
large: 'w-11 h-11 hover:scale-110',
},
active: {
true: '',
false: '',
},
},
compoundVariants: [
{
variant: 'primary',
active: true,
class: 'bg-red-400/80 hover:bg-red-500/80 focus:bg-red-500/80',
},
{
variant: 'primary',
active: false,
class: 'bg-accent-300/70 hover:bg-accent-400/70 focus:bg-accent-700',
},
],
defaultVariants: {
variant: 'primary',
size: 'base',
active: false,
},
}
);
type TimeTrackerVariants = VariantProps<typeof timeTrackerVariants>;
const emit = defineEmits(['changed']);
const props = withDefaults(
defineProps<{
size?: 'base' | 'large' | 'small';
variant?: TimeTrackerVariants['variant'];
size?: TimeTrackerVariants['size'];
active?: boolean;
}>(),
{
variant: 'primary',
size: 'base',
active: false,
}
);
const buttonSizeClasses = {
small: 'w-6 h-6 bg-accent-200/40 hover:bg-accent-300/70',
base: 'w-8 h-8 bg-accent-200/40 hover:scale-110 hover:bg-accent-300/70 ring-accent-200/10 focus-visible:ring-ring ring-4 hover:ring-4',
large: 'w-11 h-11 ring-accent-200/10 focus-visible:ring-ring focus-visible:ring-2 ring-4 sm:ring-[6px] hover:scale-110',
};
const iconClass = {
small: 'w-2.5 h-2.5',
base: 'w-3.5 h-3.5',
base: 'w-3 h-3',
large: 'w-4 h-4',
};
const buttonColorClasses = computed(() => {
if (props.active) {
return 'bg-red-400/80 hover:bg-red-500/80 focus:bg-red-500/80';
} else {
return 'bg-accent-300/70 hover:bg-accent-400/70 focus:bg-accent-700';
}
});
function toggleState() {
emit('changed', !props.active);
}
@@ -41,18 +75,12 @@ function toggleState() {
<template>
<button
data-testid="timer_button"
:class="
twMerge(
buttonSizeClasses[size],
buttonColorClasses,
'flex items-center justify-center py-1 transition focus:outline-0 rounded-full text-white '
)
"
:class="cn(timeTrackerVariants({ variant, size, active }))"
@click="toggleState">
<Transition name="fade" mode="out-in">
<svg
v-if="props.active"
:class="iconClass[size]"
:class="iconClass[size ?? 'base']"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg">
@@ -64,7 +92,7 @@ function toggleState() {
</svg>
<svg
v-else
:class="iconClass[size]"
:class="iconClass[size ?? 'base']"
viewBox="0 0 7 8"
fill="none"
xmlns="http://www.w3.org/2000/svg">

View File

@@ -17,7 +17,7 @@
--color-bg-primary: oklch(0.14 0.0041 285.97);
--color-bg-secondary: oklch(0.18 0.005 285.97);
--color-bg-tertiary: oklch(0.22 0.0112 285.97);
--color-bg-quaternary: oklch(0.175 0.005 285.97);
--color-bg-quaternary: oklch(0.26 0.015 285.97);
--color-bg-background: oklch(0.1 0 0);
--color-text-primary: #ffffff;
--color-text-secondary: #e3e4e6;