mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-06 14:11:35 +02:00
Tailwind v4 compiles group-hover: inside @media (hover: hover) - the shipped CSS has .group-hover\:opacity-100 sitting in exactly that block. On a touch-only device the media query never matches, so the rule that reveals the control is not merely never triggered: it is never applied. A control written as opacity-0 group-hover:opacity-100 is invisible for good. The reporter's iPhone screenshot shows the project card with nothing where the "..." belongs, and Edit and Delete live only there. So the hiding half is what has to depend on the pointer, not the revealing half. A can-hover variant carries the query - the same one the history thumbnail preview has used since it was written - and the six controls behind it become can-hover:opacity-0 group-hover:opacity-100. Without a hover-capable pointer no rule hides them and they simply render; with one, nothing changes. Every reveal selector is specificity (0,2,0) against the hider's (0,1,0), so which one wins does not depend on where they land in the stylesheet. Six controls were affected: the project card menu, the File Manager's folder actions (reachable by accident today - the "wrap names" toggle drops the hover class), duplicate preset, rename and delete tag, delete print photo, delete plate reference. Six more already tried to handle this, by viewport width under 768px on the Archives cards and the File Manager's file cards. That covers a phone and misses an iPad in landscape, which is touch-only at 1024px. They move to the capability check and useIsMobile goes with them, along with the isMobile prop threaded into FileCard. opacity-0 also leaves a button focusable while invisible, so tabbing through a card stopped on a control nobody could see. Focus now reveals them, through group-focus-within on the wrappers and focus-visible on the standalone buttons - neither is hover-gated. jsdom does not evaluate media queries, so the tests pin the class contract instead: a bare opacity-0 is the defect, because it applies unconditionally while everything that undoes it does not. The decorative hover reveals are left alone - the archive hash badge, the project name overlay, the thumbnail preview, the swatch tooltips. Nothing is unreachable there, only unseen.
React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- @vitejs/plugin-react uses Babel (or oxc when used in rolldown-vite) for Fast Refresh
- @vitejs/plugin-react-swc uses SWC for Fast Refresh
React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.
Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])