.ai/principles/distilled/frontend-vue.md
components/, graphql/, utils/, router/, constants.js, index.js.vue files for Vue templates; DO NOT use %template in HAMLprovide/props at instantiation)<my-component /> not <MyComponent />)<style> tags in Vue components; use Tailwind CSS utility classes or page-specific CSS insteadparseBoolean~/pinia/instanceactions.js, state.js, getters.jstryStore only during Vuex migration, then refactorcreateTestingPinia with stubActions: false when unit testing Pinia storesPiniaVuePlugin and provide the Pinia instance explicitly to shallowMount/mount in component testsforEach when mutating data; use map, reduce, or filter insteadparseInt; prefer Number for simple conversionsjs-~/ absolute paths for two or more levels upwindow.MyClass)DOMContentLoaded in non-page modules (only allowed in /pages/*)innerHTML, append(), or html() to set content (XSS risk)exportVARIANT_WARNING = 'warning') rather than exporting objects; only export as a collection when iteration is neededparseErrorMessage from ~/lib/utils/error_message to handle user-facing vs. generic server errors"strict": true in tsconfig.jsonany; use unknown with type narrowing or well-defined types<> or as; use type predicates insteadinterface over type for new structurestype only for aliases of existing types/interfacesprovide/inject or constructor parameters for dependency injection instead of singletonsaxios_utils (not directly from axios) to ensure CSRF token is setapollo-client for GraphQL API callsaxios-mock-adapter (not spyOn) to mock Axios responses in tests{} as the third argument when mocking poll requests__(), s__(), n__() from ~/locale for JavaScript/Vue translations%{linkStart}/%{linkEnd}) insteadGlSprintf instances; keep the full sentence in a single GlSprintf :messageGlSprintf when including child components or HTML in translation stringssprintf for simple variable interpolation in computed propertiesWorkItemsStatusConfigure|Add to) over broad ones (e.g., WorkItems|Add to)downcase or toLocaleLowerCase() on translatable strings; let translators control casing%{linkStart}/%{linkEnd} placeholder pairsi18n object on the Vue component (e.g., $options.i18n.myString) instead of inlining s__()/__() calls directly in <template>__() or s__(); use plain string literals (i18n is mocked in the test environment)tooling/bin/gettext_extractor locale/gitlab.pot after adding new translatable strings:base instead so Rails does not prepend the humanized attribute namecreateComponent factory functionmountExtended / shallowMountExtended helpers to access wrapper.findByTestId()createComponent parametersdata, methods, or other options that extend component internals in createComponentpropsData at mount time; DO NOT use setProps except when testing reactivitysetData; trigger events or side-effects to force state changeswrapper.props('myProp') over wrapper.props().myProp or wrapper.vm.myProptoEqual when asserting multiple props; use toMatchObject over expect.objectContaining for partial prop checksassertProps helper to test props validation failuresstubs option to properly stub async child components in shallowMount; ensure async child components have a name optiongl-border gl-rounded-lg gl-p-5) over container components<gitlab-experiment> componentexperiment type feature flag (not development or ops)actor:, project:, group:)/ee/app/assets/images or /app/assets/images, not Pajamas libraryFor the full picture, see: