From ea871e3fb4fea231650004085c3479c5d6011fd6 Mon Sep 17 00:00:00 2001 From: Dave Gallant Date: Sat, 14 Feb 2026 22:58:50 -0500 Subject: [PATCH] Remove vuetify and add theme toggle button --- package.json | 8 +- src/App.vue | 40 ++++----- src/main.js | 6 +- src/plugins/README.md | 3 - src/plugins/index.js | 12 --- src/plugins/vuetify.js | 68 -------------- src/theme.css | 197 ++++++++++++++++------------------------- vite.config.mjs | 13 +-- 8 files changed, 103 insertions(+), 244 deletions(-) delete mode 100644 src/plugins/README.md delete mode 100644 src/plugins/index.js delete mode 100644 src/plugins/vuetify.js diff --git a/package.json b/package.json index 3ec789f..fa4943d 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,7 @@ "dayjs": "^1.11.10", "vue": "^3.5.17", "vue-loading-overlay": "^6.0.3", - "vue-router": "^5.0.0", - "vuetify": "^3.9.6" + "vue-router": "^5.0.0" }, "devDependencies": { "@babel/core": "^7.22.10", @@ -34,8 +33,7 @@ "postcss-cli": "^11.0.0", "sass-embedded": "^1.89.2", "unplugin-vue-components": "^31.0.0", - "vite": "^6.3.6", - "vite-plugin-vuetify": "^2.1.1" + "vite": "^6.3.6" }, "eslintConfig": { "root": true, @@ -51,4 +49,4 @@ }, "rules": {} } -} +} \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index f135d09..85a6e04 100644 --- a/src/App.vue +++ b/src/App.vue @@ -28,7 +28,7 @@ export default { window.addEventListener("keydown", this.handleKeyDown); this.detectMobile(); this.fetchDeals(); - // Initialize theme on next tick to ensure Vuetify is ready + // Initialize theme on next tick this.$nextTick(() => { this.initializeTheme(); this.setupThemeListener(); @@ -44,14 +44,15 @@ export default { methods: { initializeTheme() { // If no saved preference, apply system preference now - const savedTheme = localStorage.getItem('vuetify-theme'); + const savedTheme = localStorage.getItem('theme'); if (!savedTheme) { const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const theme = prefersDark ? 'dark' : 'light'; this.applyTheme(theme); } else { - // Get current theme name from Vuetify - this.currentTheme = this.$vuetify.theme.global.name; + this.currentTheme = savedTheme; + // Apply saved theme + this.applyTheme(savedTheme); } }, setupThemeListener() { @@ -63,7 +64,7 @@ export default { // Use arrow function to preserve 'this' context const themeChangeHandler = (e) => { // Only auto-update theme if user hasn't set a preference manually - const savedTheme = localStorage.getItem('vuetify-theme'); + const savedTheme = localStorage.getItem('theme'); if (!savedTheme) { const newTheme = e.matches ? 'dark' : 'light'; console.log('System theme changed to:', newTheme); @@ -77,10 +78,8 @@ export default { this.darkModeQuery = darkModeQuery; }, applyTheme(theme) { - // Apply theme using Vuetify's theme API (using .value for reactive ref) - this.$vuetify.theme.global.name.value = theme; this.currentTheme = theme; - localStorage.setItem('vuetify-theme', theme); + localStorage.setItem('theme', theme); // Update data-bs-theme attribute for CSS variables to work document.documentElement.setAttribute('data-bs-theme', theme === 'dark' ? 'dark' : 'light'); @@ -192,21 +191,25 @@ export default { - - diff --git a/src/main.js b/src/main.js index 99a424c..ddd42dd 100644 --- a/src/main.js +++ b/src/main.js @@ -4,8 +4,6 @@ import { createRouter, createWebHashHistory } from "vue-router"; import "./theme.css"; -import { registerPlugins } from "@/plugins"; - const routes = [ { path: '/:pathMatch(.*)*', @@ -20,7 +18,5 @@ const router = createRouter({ const app = createApp(App); -registerPlugins(app); - app.use(router); -app.mount("#app"); +app.mount("#app"); \ No newline at end of file diff --git a/src/plugins/README.md b/src/plugins/README.md deleted file mode 100644 index 62201c7..0000000 --- a/src/plugins/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Plugins - -Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally. diff --git a/src/plugins/index.js b/src/plugins/index.js deleted file mode 100644 index 705f228..0000000 --- a/src/plugins/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * plugins/index.js - * - * Automatically included in `./src/main.js` - */ - -// Plugins -import vuetify from './vuetify' - -export function registerPlugins (app) { - app.use(vuetify) -} diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js deleted file mode 100644 index 29bec4e..0000000 --- a/src/plugins/vuetify.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * plugins/vuetify.js - * - * Framework documentation: https://vuetifyjs.com - */ - -// Styles -import "@mdi/font/css/materialdesignicons.css"; -import "vuetify/styles"; - -// Composables -import { createVuetify } from "vuetify"; - -const lightTheme = { - dark: false, - colors: { - background: "#ffffff", - surface: "#f5f5f5", - primary: "#1976d2", - secondary: "#424242", - accent: "#82b1ff", - error: "#f44336", - info: "#2196f3", - success: "#4caf50", - warning: "#ff9800", - }, -}; - -const darkTheme = { - dark: true, - colors: { - background: "#1a1a1a", - surface: "#2a2a2a", - primary: "#5b9cf5", - secondary: "#a0a0a0", - accent: "#7aa2f7", - error: "#f87171", - info: "#60a5fa", - success: "#4ade80", - warning: "#facc15", - }, -}; - -function getDefaultTheme() { - // Check for saved theme preference - const savedTheme = localStorage.getItem('vuetify-theme'); - if (savedTheme) { - return savedTheme; - } - - // Check system preference - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - return prefersDark ? 'dark' : 'light'; -} - -// Create vuetify instance -const vuetify = createVuetify({ - theme: { - defaultTheme: getDefaultTheme(), - themes: { - light: lightTheme, - dark: darkTheme, - }, - }, -}); - -// Export the vuetify instance so other parts of the app can access it -export { vuetify as default }; \ No newline at end of file diff --git a/src/theme.css b/src/theme.css index edefbe7..8681804 100644 --- a/src/theme.css +++ b/src/theme.css @@ -42,7 +42,7 @@ html[data-bs-theme="dark"] { --text-primary: #e0e0e0; --text-secondary: #a0a0a0; --border-color: #3a3a3a; - --link-color: ##e0e0e0; + --link-color: #e0e0e0; } html[data-bs-theme="light"], @@ -98,76 +98,7 @@ a:visited { color: var(--link-color); } -@media (min-width: 769px) { - .v-data-table-header th, - .v-data-table__td { - border-color: #888888 !important; - } -} - -/* Vuetify table dark theme overrides */ -html.dark-theme .v-data-table { - background-color: #1a1a1a !important; - color: #e0e0e0 !important; -} - -html.dark-theme .v-data-table__tr { - background-color: #1a1a1a !important; - color: #e0e0e0 !important; -} - -html.dark-theme .v-data-table__td { - background-color: #1a1a1a !important; - color: #e0e0e0 !important; - border-color: #888888 !important; -} - -html.dark-theme .v-data-table-header { - background-color: #2a2a2a !important; -} - -html.dark-theme .v-data-table-header th { - background-color: #2a2a2a !important; - color: #e0e0e0 !important; - border-color: #888888 !important; -} - -html.dark-theme .v-data-table__divider { - border-color: #888888 !important; -} - -html.dark-theme .v-table__wrapper { - background-color: #1a1a1a !important; -} - -/* Light theme table overrides */ -html.light-theme .v-data-table { - background-color: #ffffff !important; - color: #212529 !important; -} - -html.light-theme .v-data-table__tr { - background-color: #ffffff !important; - color: #212529 !important; -} - -html.light-theme .v-data-table__td { - background-color: #ffffff !important; - color: #212529 !important; - border-color: #dee2e6 !important; -} - -html.light-theme .v-data-table-header { - background-color: #f5f5f5 !important; -} - -html.light-theme .v-data-table-header th { - background-color: #f5f5f5 !important; - color: #212529 !important; - border-color: #dee2e6 !important; -} - -/* App.vue scoped styles */ +/* App styles */ .container { max-width: 1200px; @@ -182,9 +113,82 @@ html.light-theme .v-data-table-header th { margin-bottom: 30px; } +.header-controls { + display: flex; + gap: 12px; + align-items: center; +} + .search-input { - width: 100%; + flex: 1; max-width: 500px; + padding: 10px 12px; + font-size: 14px; + border: 1px solid #cccccc; + border-radius: 4px; + background-color: #f5f5f5; + color: var(--text-primary); + transition: all 0.2s ease; + font-family: inherit; +} + +.search-input:focus { + outline: none; + border-color: #999999; + background-color: #ffffff; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05); +} + +html.dark-theme .search-input { + border-color: #555555; + background-color: #1a1a1a; + color: #e0e0e0; +} + +html.dark-theme .search-input:focus { + background-color: #2a2a2a; + border-color: #777777; + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); +} + +.search-input::placeholder { + color: var(--text-secondary); +} + +.theme-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border: 1px solid #cccccc; + border-radius: 4px; + background-color: #f5f5f5; + color: var(--text-primary); + cursor: pointer; + transition: all 0.2s ease; + font-size: 18px; + padding: 0; +} + +.theme-toggle:hover { + background-color: #e8e8e8; + border-color: #999999; +} + +.theme-toggle:active { + transform: scale(0.95); +} + +html.dark-theme .theme-toggle { + border-color: #555555; + background-color: #1a1a1a; + color: #e0e0e0; +} + +html.dark-theme .theme-toggle:hover { + background-color: #2a2a2a; + border-color: #777777; } .cards-grid { @@ -237,7 +241,7 @@ html.light-theme .v-data-table-header th { } .deal-title:visited { - color: var(--link-visited); + color: var(--link-color); } .deal-title:hover { @@ -404,50 +408,3 @@ html.dark-theme mark { font-weight: 600; border-radius: 2px; } - -/* Vuetify overrides */ -.v-text-field { - --v-field-border-color: #cccccc; -} - -html[data-bs-theme="light"] .v-text-field { - --v-field-border-color: #e8e8e8; -} - -html[data-bs-theme="light"] .v-field__input { - background-color: #d0d0d0 !important; -} - -html[data-bs-theme="light"] .v-field--focused .v-field__input { - background-color: #e8e8e8 !important; -} - -html[data-bs-theme="dark"] .v-text-field { - --v-field-border-color: #555555; -} - -html.light-theme .v-text-field { - --v-field-border-color: #cccccc; -} - -html.light-theme .v-field__input { - background-color: #d0d0d0 !important; -} - -html.light-theme .v-field--focused .v-field__input { - background-color: #e8e8e8 !important; -} - -html.dark-theme .v-text-field { - --v-field-border-color: #555555; -} - -/* Ensure v-app and v-main use theme colors */ -.v-app { - background-color: var(--bg-primary) !important; - color: var(--text-primary) !important; -} - -.v-main { - background-color: var(--bg-primary) !important; -} \ No newline at end of file diff --git a/vite.config.mjs b/vite.config.mjs index 651f261..c8111ba 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -1,7 +1,6 @@ // Plugins import Components from "unplugin-vue-components/vite"; import Vue from "@vitejs/plugin-vue"; -import Vuetify, { transformAssetUrls } from "vite-plugin-vuetify"; // Utilities import { defineConfig } from "vite"; @@ -10,16 +9,9 @@ import { fileURLToPath, URL } from "node:url"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ - Vue({ - template: { transformAssetUrls }, - }), - Vuetify(), + Vue(), Components(), ], - optimizeDeps: { - exclude: ["vuetify"], - include: ["axios", "vue-router", "vue-loading-overlay"], - }, define: { "process.env": {} }, resolve: { alias: { @@ -54,7 +46,6 @@ export default defineConfig({ rollupOptions: { output: { manualChunks: { - "vuetify": ["vuetify"], "vendor": ["axios", "dayjs", "vue-router", "vue-loading-overlay"], }, chunkFileNames: "js/[name].[hash].js", @@ -76,4 +67,4 @@ export default defineConfig({ chunkSizeWarningLimit: 1000, reportCompressedSize: true, }, -}); +}); \ No newline at end of file