# Using Components
We distinguish between components that are coupled with funkwhale-specific datatypes and "pure" user interface components:
- Funkwhale-specific components such as `Activity` or `AlbumCard` import from `types.ts`.
- Pure UI components (found in `src/components/ui`) are independent from Funkwhale. Think of `Button`, `Tabs` or `Layout`
---
[[toc]]
## Anatomy of a component file
### Imports
First, import vue features and external libraries. Add the sub-components you want to use last. Order each block of imports by alphabet to prevent commit diff noise.
### Script
Add a blank line between Imports and script. Use modern typescript-friendly features such as `defineModel` and `defineProps` [as documented in the Vue Docs](https://vuejs.org/api/sfc-script-setup.html#definemodel) instead of Macros.
### Template
If you are new to Vue, read the docs, especially [the chapter about Single-File Components](https://vuejs.org/guide/scaling-up/sfc), to get familiar.
### Style
Don't pollute the global namespace. Funkwhale compiles a single stylesheet (used in the app, the blog and the website). If you need specific styles in your component, use vue's [SFC features](https://vuejs.org/api/sfc-css-features.html#sfc-css-features) such as `module`. Vue will give you a `$style` object containing all locally defined classes.
```vue
```
::: details Tip: Debugging styles
We have enabled [the vite feature `css.devSourcemap: true`](https://v2.vitejs.dev/config/#css-devsourcemap) so that in your browser devtools, you can trace the code responsible for module styles:
:::
::: info What about the global style?
As of now, class and variable names from the global styles are not available as typescript objects. We should definitely add this feature at some point.
:::
## Using UI components in your views
```vue
```
## Limitations of component props
While Vue can infer props based on a type, it will fail with mysterious errors if the type is an exclusive union or has union types as keys.
I hope this will be resolved soon so we can use this more elegant way of injecting non-trivial props with full autocomplete, 21st century style:
```vue
{{ Error: }} {{ Error: }}
```
::: details Example
````ts
// Color from props
type SingleOrNoProp = RequireOneOrNone, T>;
type SingleProp = RequireExactlyOne, T>;
export type Props = Simplify<
SingleProp &
SingleOrNoProp &
SingleOrNoProp<"interactive"> &
SingleOrNoProp<"raised">
>;
// Limit the choices:
export type ColorProps = Simplify<
SingleProp & SingleOrNoProp & SingleOrNoProp<"raised">
>;
export type PastelProps = Simplify<
SingleProp & SingleOrNoProp<"raised">
>;
// Note that as of now, Vue does not support unions of props.
// So instead, we give it a single string:
export type ColorProp = Simplify<`${Color}${
| ""
| `-${Variant}${"" | "-raised"}`}`>;
export type PastelProp = Simplify<`${Pastel}${"" | "-raised"}`>;
// Using like this:
// type Props = {...} & { [k in ColorProp]? : true }
// This will also lead to runtime errors. Why?
export const isColorProp = (k: string) =>
!![...colors, ...defaults, ...pastels].find(k.startsWith);
console.log(true, isColorProp("primary"));
console.log(true, isColorProp("secondary"));
console.log(true, isColorProp("red"));
console.log(false, isColorProp("Jes"));
console.log(false, isColorProp("raised"));
/**
* Convenience function in case you want to hand over the props in the form
* ```
* ...
* ```
*
* @param props Any superset of type `Props`
* @returns the corresponding `class` object
*
* Note: Make sure to implement the necessary classes in `colors.scss`!
*/
export const colorFromProps = (props: Record) =>
color(
Object.keys(props)
.filter(isColorProp)
.join(" ")
.replace("-", " ") as ColorSelector
);
````
:::