From a8d2103980da04aa473f4ba7c5c20b9392c23027 Mon Sep 17 00:00:00 2001 From: Mar0xy <marie@kaifa.ch> Date: Wed, 4 Oct 2023 03:36:26 +0200 Subject: [PATCH] upd: add MFM Cheatsheet Closes transfem-org/Sharkey#21 --- locales/en-US.yml | 73 +++ .../frontend/src/components/MkMfmWindow.vue | 476 ++++++++++++++++++ .../frontend/src/components/MkPostForm.vue | 7 +- 3 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 packages/frontend/src/components/MkMfmWindow.vue diff --git a/locales/en-US.yml b/locales/en-US.yml index 24fece3e40..2e6cb32e31 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2138,3 +2138,76 @@ _moderationLogTypes: createAd: "Ad created" deleteAd: "Ad deleted" updateAd: "Ad updated" +_mfm: + intro: "MFM is a markup language used on Misskey, Sharkey, Firefish, Akkoma, and more that can be used in many places. Here you can view a list of all available MFM syntax." + dummy: "Sharkey expands the world of the Fediverse" + mention: "Mention" + mentionDescription: "You can specify a user by using an At-Symbol and a username." + hashtag: "Hashtag" + hashtagDescription: "You can specify a hashtag using a number sign and text." + url: "URL" + urlDescription: "URLs can be displayed." + link: "Link" + linkDescription: "Specific parts of text can be displayed as a URL." + bold: "Bold" + boldDescription: "Highlights letters by making them thicker." + small: "Small" + smallDescription: "Displays content small and thin." + center: "Center" + centerDescription: "Displays content centered." + inlineCode: "Code (Inline)" + inlineCodeDescription: "Displays inline syntax highlighting for (program) code." + blockCode: "Code (Block)" + blockCodeDescription: "Displays syntax highlighting for multi-line (program) code in a block." + inlineMath: "Math (Inline)" + inlineMathDescription: "Display math formulas (KaTeX) in-line" + blockMath: "Math (Block)" + blockMathDescription: "Display math formulas (KaTeX) in a block" + quote: "Quote" + quoteDescription: "Displays content as a quote." + emoji: "Custom Emoji" + emojiDescription: "By surrounding a custom emoji name with colons, custom emoji can be displayed." + search: "Search" + searchDescription: "Displays a search box with pre-entered text." + flip: "Flip" + flipDescription: "Flips content horizontally or vertically." + jelly: "Animation (Jelly)" + jellyDescription: "Gives content a jelly-like animation." + tada: "Animation (Tada)" + tadaDescription: "Gives content a \"Tada!\"-like animation." + jump: "Animation (Jump)" + jumpDescription: "Gives content a jumping animation." + bounce: "Animation (Bounce)" + bounceDescription: "Gives content a bouncy animation." + shake: "Animation (Shake)" + shakeDescription: "Gives content a shaking animation." + twitch: "Animation (Twitch)" + twitchDescription: "Gives content a strongly twitching animation." + spin: "Animation (Spin)" + spinDescription: "Gives content a spinning animation." + x2: "Big" + x2Description: "Displays content bigger." + x3: "Very big" + x3Description: "Displays content even bigger." + x4: "Unbelievably big" + x4Description: "Displays content even bigger than bigger than big." + blur: "Blur" + blurDescription: "Blurs content. It will be displayed clearly when hovered over." + font: "Font" + fontDescription: "Sets the font to display content in." + rainbow: "Rainbow" + rainbowDescription: "Makes the content appear in rainbow colors." + sparkle: "Sparkle" + sparkleDescription: "Gives content a sparkling particle effect." + rotate: "Rotate" + rotateDescription: "Turns content by a specified angle." + position: "Position" + positionDescription: "Move content by a specified amount." + scale: "Scale" + scaleDescription: "Scale content by a specified amount." + foreground: "Foreground color" + foregroundDescription: "Change the foreground color of text." + background: "Background color" + backgroundDescription: "Change the background color of text." + plain: "Plain" + plainDescription: "Deactivates the effects of all MFM contained within this MFM effect." diff --git a/packages/frontend/src/components/MkMfmWindow.vue b/packages/frontend/src/components/MkMfmWindow.vue new file mode 100644 index 0000000000..eb0ccc560a --- /dev/null +++ b/packages/frontend/src/components/MkMfmWindow.vue @@ -0,0 +1,476 @@ +<template> +<MkWindow + ref="window" + :initialWidth="600" + :initialHeight="800" + :canResize="true" + @closed="emit('closed')" +> + <template #header> + MFM Cheatsheet + </template> + <MkStickyContainer> + <MkSpacer :content-max="800"> + <div class="mfm-cheat-sheet"> + <div>{{ i18n.ts._mfm.intro }}</div> + <br /> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.mention }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.mentionDescription }}</p> + <div class="preview"> + <Mfm :text="preview_mention" /> + <MkTextarea v-model="preview_mention"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.hashtag }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.hashtagDescription }}</p> + <div class="preview"> + <Mfm :text="preview_hashtag" /> + <MkTextarea v-model="preview_hashtag"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.link }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.linkDescription }}</p> + <div class="preview"> + <Mfm :text="preview_link" /> + <MkTextarea v-model="preview_link"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.emoji }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.emojiDescription }}</p> + <div class="preview"> + <Mfm :text="preview_emoji" /> + <MkTextarea v-model="preview_emoji"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.bold }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.boldDescription }}</p> + <div class="preview"> + <Mfm :text="preview_bold" /> + <MkTextarea v-model="preview_bold"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.small }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.smallDescription }}</p> + <div class="preview"> + <Mfm :text="preview_small" /> + <MkTextarea v-model="preview_small"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.quote }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.quoteDescription }}</p> + <div class="preview"> + <Mfm :text="preview_quote" /> + <MkTextarea v-model="preview_quote"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.center }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.centerDescription }}</p> + <div class="preview"> + <Mfm :text="preview_center" /> + <MkTextarea v-model="preview_center"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.inlineCode }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.inlineCodeDescription }}</p> + <div class="preview"> + <Mfm :text="preview_inlineCode" /> + <MkTextarea v-model="preview_inlineCode"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.blockCode }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.blockCodeDescription }}</p> + <div class="preview"> + <Mfm :text="preview_blockCode" /> + <MkTextarea v-model="preview_blockCode"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.inlineMath }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.inlineMathDescription }}</p> + <div class="preview"> + <Mfm :text="preview_inlineMath" /> + <MkTextarea v-model="preview_inlineMath"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.blockMath }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.blockMathDescription }}</p> + <div class="preview"> + <Mfm :text="preview_blockMath" /> + <MkTextarea v-model="preview_blockMath"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.search }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.searchDescription }}</p> + <div class="preview"> + <Mfm :text="preview_search" /> + <MkTextarea v-model="preview_search"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.flip }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.flipDescription }}</p> + <div class="preview"> + <Mfm :text="preview_flip" /> + <MkTextarea v-model="preview_flip"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.font }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.fontDescription }}</p> + <div class="preview"> + <Mfm :text="preview_font" /> + <MkTextarea v-model="preview_font"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.x2 }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.x2Description }}</p> + <div class="preview"> + <Mfm :text="preview_x2" /> + <MkTextarea v-model="preview_x2"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.x3 }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.x3Description }}</p> + <div class="preview"> + <Mfm :text="preview_x3" /> + <MkTextarea v-model="preview_x3"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.x4 }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.x4Description }}</p> + <div class="preview"> + <Mfm :text="preview_x4" /> + <MkTextarea v-model="preview_x4"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.blur }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.blurDescription }}</p> + <div class="preview"> + <Mfm :text="preview_blur" /> + <MkTextarea v-model="preview_blur"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.jelly }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.jellyDescription }}</p> + <div class="preview"> + <Mfm :text="preview_jelly" /> + <MkTextarea v-model="preview_jelly"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.tada }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.tadaDescription }}</p> + <div class="preview"> + <Mfm :text="preview_tada" /> + <MkTextarea v-model="preview_tada"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.jump }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.jumpDescription }}</p> + <div class="preview"> + <Mfm :text="preview_jump" /> + <MkTextarea v-model="preview_jump"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.bounce }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.bounceDescription }}</p> + <div class="preview"> + <Mfm :text="preview_bounce" /> + <MkTextarea v-model="preview_bounce"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.spin }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.spinDescription }}</p> + <div class="preview"> + <Mfm :text="preview_spin" /> + <MkTextarea v-model="preview_spin"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.shake }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.shakeDescription }}</p> + <div class="preview"> + <Mfm :text="preview_shake" /> + <MkTextarea v-model="preview_shake"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.twitch }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.twitchDescription }}</p> + <div class="preview"> + <Mfm :text="preview_twitch" /> + <MkTextarea v-model="preview_twitch"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.rainbow }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.rainbowDescription }}</p> + <div class="preview"> + <Mfm :text="preview_rainbow" /> + <MkTextarea v-model="preview_rainbow"><template #label>MFM</template></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.sparkle }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.sparkleDescription }}</p> + <div class="preview"> + <Mfm :text="preview_sparkle" /> + <MkTextarea v-model="preview_sparkle"><span>MFM</span></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.rotate }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.rotateDescription }}</p> + <div class="preview"> + <Mfm :text="preview_rotate" /> + <MkTextarea v-model="preview_rotate"><span>MFM</span></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.position }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.positionDescription }}</p> + <div class="preview"> + <Mfm :text="preview_position" /> + <MkTextarea v-model="preview_position"><span>MFM</span></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.scale }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.scaleDescription }}</p> + <div class="preview"> + <Mfm :text="preview_scale" /> + <MkTextarea v-model="preview_scale"><span>MFM</span></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.foreground }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.foregroundDescription }}</p> + <div class="preview"> + <Mfm :text="preview_fg" /> + <MkTextarea v-model="preview_fg"><span>MFM</span></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.background }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.backgroundDescription }}</p> + <div class="preview"> + <Mfm :text="preview_bg" /> + <MkTextarea v-model="preview_bg"><span>MFM</span></MkTextarea> + </div> + </div> + </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.plain }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.plainDescription }}</p> + <div class="preview"> + <Mfm :text="preview_plain" /> + <MkTextarea v-model="preview_plain"><span>MFM</span></MkTextarea> + </div> + </div> + </div> + </div> + </MkSpacer> + </MkStickyContainer> +</MkWindow> +</template> + +<script lang="ts" setup> +import { ref } from 'vue'; +import MkWindow from '@/components/MkWindow.vue'; +import MkTextarea from '@/components/MkTextarea.vue'; +import { i18n } from "@/i18n.js"; +import { instance } from "@/instance.js"; + +const emit = defineEmits<{ + (ev: 'closed'): void; +}>(); + +const preview_mention = ref("@example"); +const preview_hashtag = ref("#test"); +const preview_link = ref(`[${i18n.ts._mfm.dummy}](https://joinsharkey.org)`); +const preview_emoji = ref( + instance.emojis.length ? `:${instance.emojis[0].name}:` : ":emojiname:", +); +const preview_bold = ref(`**${i18n.ts._mfm.dummy}**`); +const preview_small = ref( + `<small>${i18n.ts._mfm.dummy}</small> $[small ${i18n.ts._mfm.dummy}]`, +); +const preview_center = ref( + `<center>${i18n.ts._mfm.dummy}</center>`, +); +const preview_inlineCode = ref('`<: "Hello, world!"`'); +const preview_blockCode = ref( + '```\n~ (#i, 100) {\n\t<: ? ((i % 15) = 0) "FizzBuzz"\n\t\t.? ((i % 3) = 0) "Fizz"\n\t\t.? ((i % 5) = 0) "Buzz"\n\t\t. i\n}\n```', +); +const preview_inlineMath = ref( + "\\(x= \\frac{-b' \\pm \\sqrt{(b')^2-ac}}{a}\\)", +); +const preview_blockMath = ref("\\[x= \\frac{-b' \\pm \\sqrt{(b')^2-ac}}{a}\\]"); +const preview_quote = ref(`> ${i18n.ts._mfm.dummy}`); +const preview_search = ref( + `${i18n.ts._mfm.dummy} [search]\n${i18n.ts._mfm.dummy} [ๆค็ดข]\n${i18n.ts._mfm.dummy} ๆค็ดข`, +); +const preview_jelly = ref( + "$[jelly ๐ฎ] $[jelly.speed=3s ๐ฎ] $[jelly.delay=3s ๐ฎ] $[jelly.loop=3 ๐ฎ]", +); +const preview_tada = ref( + "$[tada ๐ฎ] $[tada.speed=3s ๐ฎ] $[tada.delay=3s ๐ฎ] $[tada.loop=3 ๐ฎ]", +); +const preview_jump = ref( + "$[jump ๐ฎ] $[jump.speed=3s ๐ฎ] $[jump.delay=3s ๐ฎ] $[jump.loop=3 ๐ฎ]", +); +const preview_bounce = ref( + "$[bounce ๐ฎ] $[bounce.speed=3s ๐ฎ] $[bounce.delay=3s ๐ฎ] $[bounce.loop=3 ๐ฎ]", +); +const preview_shake = ref( + "$[shake ๐ฎ] $[shake.speed=3s ๐ฎ] $[shake.delay=3s ๐ฎ] $[shake.loop=3 ๐ฎ]", +); +const preview_twitch = ref( + "$[twitch ๐ฎ] $[twitch.speed=3s ๐ฎ] $[twitch.delay=3s ๐ฎ] $[twitch.loop=3 ๐ฎ]", +); +const preview_spin = ref( + "$[spin ๐ฎ] $[spin.left ๐ฎ] $[spin.alternate ๐ฎ]\n$[spin.x ๐ฎ] $[spin.x,left ๐ฎ] $[spin.x,alternate ๐ฎ]\n$[spin.y ๐ฎ] $[spin.y,left ๐ฎ] $[spin.y,alternate ๐ฎ]\n\n$[spin.speed=3s ๐ฎ] $[spin.delay=3s ๐ฎ] $[spin.loop=3 ๐ฎ]", +); +const preview_flip = ref( + `$[flip ${i18n.ts._mfm.dummy}]\n$[flip.v ${i18n.ts._mfm.dummy}]\n$[flip.h,v ${i18n.ts._mfm.dummy}]`, +); +const preview_font = ref( + `$[font.serif ${i18n.ts._mfm.dummy}]\n$[font.monospace ${i18n.ts._mfm.dummy}]\n$[font.cursive ${i18n.ts._mfm.dummy}]\n$[font.fantasy ${i18n.ts._mfm.dummy}]`, +); +const preview_x2 = ref("$[x2 ๐ฎ]"); +const preview_x3 = ref("$[x3 ๐ฎ]"); +const preview_x4 = ref("$[x4 ๐ฎ]"); +const preview_blur = ref(`$[blur ${i18n.ts._mfm.dummy}]`); +const preview_rainbow = ref( + "$[rainbow ๐ฎ] $[rainbow.speed=3s ๐ฎ] $[rainbow.delay=3s ๐ฎ] $[rainbow.loop=3 ๐ฎ]", +); +const preview_sparkle = ref("$[sparkle ๐ฎ]"); +const preview_rotate = ref( + "$[rotate ๐ฎ]\n$[rotate.deg=45 ๐ฎ]\n$[rotate.x,deg=45 Hello, world!]", +); +const preview_position = ref("$[position.y=-1 ๐ฎ]\n$[position.x=-1 ๐ฎ]"); +const preview_scale = ref( + "$[scale.x=1.3 ๐ฎ]\n$[scale.x=1.5,y=3 ๐ฎ]\n$[scale.y=0.3 ๐ฎ]", +); +const preview_fg = ref("$[fg.color=eb6f92 Text color]"); +const preview_bg = ref("$[bg.color=31748f Background color]"); +const preview_plain = ref( + "<plain>**bold** @mention #hashtag `code` $[x2 ๐ฎ]</plain>", +); +</script> + +<style lang="scss" scoped> +.mfm-cheat-sheet { + > .section { + > .title { + position: sticky; + z-index: 1; + top: var(--stickyTop, 0px); + padding: 16px; + font-weight: bold; + -webkit-backdrop-filter: var(--blur, blur(10px)); + backdrop-filter: var(--blur, blur(10px)); + background-color: var(--X16); + } + + > .content { + > p { + margin: 0; + padding: 16px; + padding-top: 0; + } + + > .preview { + border-top: solid 0.5px var(--divider); + padding: 16px; + } + } + } +} +</style> \ No newline at end of file diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 623e79d4cc..613d93dfa7 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -88,6 +88,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div :class="$style.footerRight"> <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ph-eye ph-bold ph-lg"></i></button> + <button v-tooltip="'MFM Cheatsheet'" class="_button" :class="$style.footerButton" @click="MFMWindow"><i class="ph-notebook ph-bold ph-lg"></i></button> <!--<button v-tooltip="i18n.ts.more" class="_button" :class="$style.footerButton" @click="showingOptions = !showingOptions"><i class="ph-dots-three ph-bold ph-lg"></i></button>--> </div> </footer> @@ -346,6 +347,10 @@ function watchForDraft() { watch($$(localOnly), () => saveDraft()); } +function MFMWindow() { + os.popup(defineAsyncComponent(() => import('@/components/MkMfmWindow.vue')), {}, {}, 'closed'); +} + function checkMissingMention() { if (visibility === 'specified') { const ast = mfm.parse(text); @@ -1159,7 +1164,7 @@ defineExpose({ } .footerRight { - flex: 0; + flex: 0.3; margin-left: auto; display: grid; grid-auto-flow: row;