Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true
},
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"cssVariables.lookupFiles": ["node_modules/tailwindcss/theme.css"]
}
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@
},
"devDependencies": {
"@eslint/js": "9.20.0",
"@tailwindcss/vite": "4.0.9",
"@types/node": "22.13.7",
"@vitejs/plugin-vue": "5.2.1",
"@vue/compiler-sfc": "3.5.13",
"autoprefixer": "10.4.20",
"eslint": "9.20.1",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.2.3",
"eslint-plugin-vue": "9.32.0",
"postcss": "8.5.3",
"prettier": "3.5.2",
"tailwindcss": "3.4.17",
"prettier-plugin-tailwindcss": "0.6.11",
"tailwindcss": "4.0.9",
"typescript": "5.7.3",
"typescript-eslint": "8.24.1",
"vite": "6.1.1",
Expand All @@ -43,6 +43,9 @@
"vue-tsc": "2.2.4"
},
"prettier": {
"plugins": [
"prettier-plugin-tailwindcss"
],
"trailingComma": "all",
"arrowParens": "always",
"semi": true,
Expand Down
927 changes: 385 additions & 542 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 0 additions & 6 deletions postcss.config.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ onMounted(checkNotifyPermission);
</script>

<template>
<div role="presentation">
<div>
<TheGraphicTimer v-if="cycle.currentInterval" />
<TheNotificationBar>
<TransitionFadeSlide>
Expand Down
50 changes: 37 additions & 13 deletions src/assets/css/tailwind.css
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';

@layer base {
body {
@apply text-gray-700 dark:bg-stone-800 dark:text-gray-50;
@utility input {

& {
@apply appearance-none bg-transparent border border-gray-300 dark:border-stone-600 rounded-lg px-2 py-1 text-gray-700 dark:text-gray-200;
}
}

@layer components {
.input {
@apply appearance-none bg-transparent border border-gray-300 dark:border-stone-600 rounded-lg pl-2 py-1 text-gray-700 dark:text-gray-200;
&:is(select) {
@apply pe-6;
}
.text-gray-700[type='number'] {
-moz-appearance: textfield;

&[type='number'] {
appearance: textfield;
}
.text-gray-700[type='number']::-webkit-inner-spin-button {
&[type='number']::-webkit-inner-spin-button {
-webkit-appearance: none;
}
}


@utility grid-overlap {
& {
grid-template: 'full' auto / auto
}

& > * {
grid-area: full;
}
}

@layer theme {
@property --s {
syntax: '*';
inherits: false;
}


}

@layer base {
body {
@apply text-gray-700 dark:bg-stone-800 dark:text-gray-50;
}
}
12 changes: 11 additions & 1 deletion src/components/BaseButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@
import { oneOf } from 'vue-types';
defineProps({
type: oneOf(['button', 'reset', 'submit', undefined] as const).def('button'),
variant: oneOf(['primary', 'secondary', 'ghost'] as const).def('primary'),
size: oneOf(['base', 'sm'] as const).def('base'),
});
defineSlots<{ default?: () => unknown }>();
</script>
<template>
<button
:type="type"
class="bg-blue-700 hover:bg-blue-800 px-2 py-1 transition-colors duration-75 rounded-sm"
class="flex items-center gap-x-2.5 rounded-md transition-colors duration-75"
:class="{
'bg-blue-700 text-white hover:bg-blue-800': variant === 'primary',
'bg-blue-100 text-blue-700 hover:bg-blue-50': variant === 'secondary',
'text-blue-600 hover:bg-blue-100 dark:text-sky-400 dark:hover:bg-sky-700':
variant === 'ghost',
'px-2 py-1 text-sm': size === 'sm',
'px-3 py-2 text-base': size === 'base',
}"
>
<slot />
</button>
Expand Down
16 changes: 7 additions & 9 deletions src/components/BaseControl.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<script setup lang="ts">
import { bool, oneOf } from 'vue-types';
import { oneOf } from 'vue-types';
import { iconList } from './BaseIcon.vue';
import BaseIcon from './BaseIcon.vue';
defineProps({
pressed: bool().def(false),
type: oneOf(['button', 'reset', 'submit', undefined] as const).def('button'),
icon: oneOf(iconList).isRequired,
});
defineSlots<{ default?: () => unknown }>();
</script>
<template>
<button
:type="type"
class="rounded leading-none p-2 border-2 border-transparent text-blue-500 dark:text-sky-400 transition-colors duration-150 space-x-1"
:class="{
'bg-blue-200 hover:border-blue-500': pressed,
'hover:bg-blue-100 dark:hover:bg-sky-700': !pressed,
}"
:aria-pressed="pressed"
class="inline-flex gap-x-1 rounded-sm border-none p-3 align-middle leading-none text-blue-600 transition-colors duration-150 hover:bg-blue-100 dark:text-sky-400 dark:hover:bg-sky-700"
>
<slot />
<BaseIcon :name="icon" />
<span class="sr-only"><slot /></span>
</button>
</template>
33 changes: 17 additions & 16 deletions src/components/BaseIcon.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
<script lang="ts">
export const iconList = [
'trash',
'pause',
'fast-forward',
'reload',
'wrench',
'add-outline',
'close',
'save-disk',
'cheveron-down',
'play',
] as const;
</script>
<script setup lang="ts">
import { watch, shallowRef } from 'vue';
import type { Component } from 'vue';
import { oneOf } from 'vue-types';

const icons = import.meta.glob<{ default: Component }>(
'../assets/zondicons/{trash,pause,fast-forward,reload,wrench,add-outline,close,save-disk,cheveron-down,play}.svg',
Expand All @@ -10,18 +25,7 @@ const icons = import.meta.glob<{ default: Component }>(
);

const props = defineProps({
name: oneOf([
'trash',
'pause',
'fast-forward',
'reload',
'wrench',
'add-outline',
'close',
'save-disk',
'cheveron-down',
'play',
] as const).isRequired,
name: oneOf(iconList).isRequired,
});

const icon = shallowRef<Component>();
Expand All @@ -45,10 +49,7 @@ watch(
<template>
<component
:is="icon"
class="inline-flex items-center fill-current w-[1em] aspect-square"
class="inline-flex aspect-square w-[1em] items-center fill-current"
aria-hidden="true"
/>
</template>
<script lang="ts">
import { oneOf } from 'vue-types';
</script>
2 changes: 1 addition & 1 deletion src/components/BaseTimer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const classes = computed(() =>
<template>
<div
role="timer"
class="tabular-nums text-4xl py-4 px-8 rounded-lg border-current border-4"
class="rounded-lg border-4 px-8 py-4 text-4xl tabular-nums"
:class="classes"
:aria-label="label"
>
Expand Down
10 changes: 7 additions & 3 deletions src/components/BaseToast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ defineSlots<{ default?: () => unknown }>();
</script>
<template>
<div
class="p-2 flex items-center space-x-2 text-sm bg-blue-600 text-white"
class="flex items-center gap-2 bg-blue-600 p-2 text-sm text-white"
:role="role"
aria-live="polite"
>
<slot />
<BaseButton @click="$emit('confirm')">yes</BaseButton>
<BaseButton @click="$emit('cancel')">dismiss</BaseButton>
<BaseButton variant="secondary" size="sm" @click="$emit('confirm')"
>yes</BaseButton
>
<BaseButton variant="secondary" size="sm" @click="$emit('cancel')"
>dismiss</BaseButton
>
</div>
</template>
22 changes: 9 additions & 13 deletions src/components/IntervalEditBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ defineEmits<{
}>();
</script>
<template>
<fieldset class="min-w-0">
<fieldset class="w-full min-w-0">
<legend class="sr-only">Interval Settings</legend>
<LayoutInline class="items-center">
<label class="grid grid-flow-col items-center">
<select
v-model="type"
class="input pr-6 col-start-1 row-start-1 min-w-0 truncate"
>
<LayoutInline vertical-align="center">
<label class="grid-overlap grid grow items-center">
<select v-model="type" class="input min-w-0 truncate">
<template v-for="(value, name) in IntervalType" :key="value">
<option
v-if="value !== IntervalType.None"
Expand All @@ -45,11 +42,11 @@ defineEmits<{
</select>
<BaseIcon
name="cheveron-down"
class="pointer-events-none col-start-1 row-start-1 ml-auto mr-1"
class="pointer-events-none ms-auto me-1"
/>
<span class="sr-only">Type</span>
</label>
<label class="flex items-center">
<label class="flex items-center gap-1">
<input
v-model.number="duration"
class="input"
Expand All @@ -59,11 +56,10 @@ defineEmits<{
:size="5"
/>
<span class="sr-only">Duration</span>
<span class="text-sm pl-1">mins</span>
<span class="text-sm">mins</span>
</label>
<BaseControl @click="$emit('delete', id)">
<BaseIcon name="trash" />
<span class="sr-only">Delete</span>
<BaseControl icon="trash" @click="$emit('delete', id)">
Delete
</BaseControl>
</LayoutInline>
</fieldset>
Expand Down
29 changes: 10 additions & 19 deletions src/components/IntervalSquare.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,19 @@ const typeFormatted = computed(() => toTitleCase(props.type));
</script>
<template>
<li
class="grid text-center px-2 py-1 rounded-md border border-current transition duration-150 ease-out"
:class="[classes, { 'bg-current': current }]"
class="grid rounded-md border px-2 py-1 text-center transition-colors duration-150 ease-out *:text-gray-700"
:class="[
classes,
{
'bg-current *:dark:text-stone-800': current,
'*:dark:text-inherit': !current,
},
]"
:aria-current="current ? 'time' : undefined"
>
<b
class="text-sm text-gray-700"
:class="{
'dark:text-stone-800': current,
' dark:text-inherit': !current,
}"
aria-hidden="true"
>{{ abbr }}</b
>
<b class="text-sm" aria-hidden="true">{{ abbr }}</b>
<span class="sr-only">{{ typeFormatted }}</span>
<time
class="text-xs text-gray-700"
:datetime="durationAttr"
:class="{
'dark:text-stone-800': current,
' dark:text-inherit': !current,
}"
>
<time class="text-xs" :datetime="durationAttr">
{{ durationFormatted }}
<span class="sr-only">minutes</span>
</time>
Expand Down
23 changes: 19 additions & 4 deletions src/components/LayoutInline.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
<script setup lang="ts">
import { string, bool, oneOfType } from 'vue-types';
import { string, bool, oneOfType, oneOf } from 'vue-types';
type TagNames = keyof HTMLElementTagNameMap;
defineProps({
tag: string().def('div'),
tag: string<TagNames>().def('div' as const),
centered: bool().def(false),
verticalAlign: oneOf(['start', 'stretch', 'end', 'center'] as const).def(
'start',
),
space: oneOfType([String, Number]).def(4),
});
defineSlots<{ default?: () => unknown }>();
</script>
<template>
<component
:is="tag"
class="flex items-start min-w-0"
class="gap flex min-w-0"
:class="{
'justify-center': centered,
[`gap-${space}`]: space,
'items-center': verticalAlign === 'center',
'items-stretch': verticalAlign === 'stretch',
'items-end': verticalAlign === 'end',
'items-start': verticalAlign === 'start',
}"
:style="{
'--s': space !== 4 ? space : undefined,
}"
>
<slot />
</component>
</template>
<style scoped>
Copy link

Copilot AI Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS variable '--spacing' referenced in the column-gap calculation is undefined. Define this variable or replace it with a proper static value to ensure consistent layout spacing.

Suggested change
<style scoped>
<style scoped>
:root {
--spacing: 1rem; /* Default value for spacing */
}

Copilot uses AI. Check for mistakes.
.gap {
column-gap: calc(var(--spacing) * var(--s, 4));
}
</style>
Loading
Loading