Skip to content

Frontend notes

Things to keep in mind when writing frontend code (packages/frontend).

  • Write components with the Vue 3 Composition API (<script lang="ts" setup>). Don’t use the Options API. Refactoring old components that still use the Options API to the Composition API is welcome.
  • Prefix component names with Mk (e.g. MkNote, MkAvatar).

A component that sets its own margin causes problems. Let the code that uses the component set the margin.

When you put a cherrypick-js connection in the data of an Options API component, always wrap it in markRaw. Making it reactive unnecessarily breaks processing inside cherrypick-js and hurts performance. This does not apply to the Composition API, where reactivity is opt-in.

Unused icons are removed in production builds.

When switching icons dynamically, don’t build only part of the name, as in ti-${someVal}. The build can no longer detect which icons are used. Always write the full class name, such as ti-xxx.

nirax is engawa’s (Misskey’s) own frontend router. It is not vue-router, but it is heavily influenced by it, so learning vue-router first helps.

Routes are defined as an array of objects of the following shape.

{
name?: string;
path: string;
component: Component;
query?: Record<string, string>;
loginRequired?: boolean;
hash?: string;
children?: RouteDef[];
}

The biggest difference from vue-router is that nirax allows several routers to exist at the same time. This makes it possible, for example, to route inside an in-app window independently of the browser.

Storybook is used for UI development.

ターミナルウィンドウ
pnpm --filter cherrypick-js build
pnpm --filter frontend storybook-dev

When you create a new component (e.g. MyComponent.vue), .storybook/generate.js generates a story file (MyComponent.stories.ts) automatically.

To override the default story, create MyComponent.stories.impl.ts.

/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import MyComponent from './MyComponent.vue';
export const Default = {
render(args) {
return {
components: {
MyComponent,
},
setup() {
return {
args,
};
},
computed: {
props() {
return {
...this.args,
};
},
},
template: '<MyComponent v-bind="props" />',
};
},
args: {
foo: 'bar',
},
parameters: {
layout: 'centered',
},
} satisfies StoryObj<typeof MyComponent>;

To opt out of the automatic generation, put the following in MyComponent.stories.impl.ts.

import MyComponent from './MyComponent.vue';
void MyComponent;

You can override the component meta with MyComponent.stories.meta.ts.

export const argTypes = {
scale: {
control: {
type: 'range',
min: 1,
max: 4,
},
},
};

API requests can be mocked with msw. Define the handlers in MyComponent.stories.msw.ts.

import { HttpResponse, http } from 'msw';
export const handlers = [
http.post('/api/notes/timeline', ({ request }) => {
return HttpResponse.json([]);
}),
];

How to adjust a theme color (CSS variable) slightly.

color: hsl(from var(--MI_THEME-accent) h s calc(l + 10));
color: hsl(from var(--MI_THEME-accent) h s calc(l - 10));
color: color(from var(--MI_THEME-accent) srgb r g b / 0.5);

For the CSS variables available in themes, see Theme properties.