├── .github └── CODEOWNERS ├── .gitignore ├── .husky └── pre-commit ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── prettier.config.js ├── public ├── .well-known │ └── brave-rewards-verification.txt ├── favicon.ico └── favicon.svg ├── src ├── App.vue ├── assets │ ├── main.css │ └── prism.css ├── components │ ├── AppHeader.vue │ ├── BaseInput.vue │ ├── BaseSelect.vue │ ├── CarbonAds.vue │ ├── CodeContainer.vue │ ├── CssMarkup.vue │ ├── FlexContainer.vue │ ├── FlexItem.vue │ ├── FlexMarkup.vue │ ├── GitHubIcon.vue │ ├── HtmlMarkup.vue │ ├── Sidebar.vue │ ├── SidebarContainer.vue │ ├── SidebarItems.vue │ ├── SidebarNav.vue │ ├── SidebarNavTab.vue │ ├── Spacer.vue │ ├── TrashCan.vue │ └── TwoBeards.vue ├── main.js └── store.js ├── tailwind.config.js └── vite.config.js /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @davidleininger 2 | @mikemcbride 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | .vercel -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flexbox Playground 2 | 3 | This is a fun little web app to help people visualize flexbox properties. Head on over to https://flexbox.tech and give it a whirl. 4 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flexbox Playground 8 | 9 | 10 |
11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flexbox-playground", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "prepare": "husky install", 9 | "format": "prettier --write --ignore-unknown ." 10 | }, 11 | "dependencies": { 12 | "uuid": "^8.1.0", 13 | "vue": "^3.0.5", 14 | "vuex": "^4.0.0" 15 | }, 16 | "devDependencies": { 17 | "@tailwindcss/forms": "^0.3.2", 18 | "@vitejs/plugin-vue": "^1.2.2", 19 | "@vue/compiler-sfc": "^3.0.5", 20 | "autoprefixer": "^10.2.5", 21 | "husky": "^8.0.0", 22 | "lint-staged": "^13.0.3", 23 | "postcss": "^8.2.15", 24 | "prettier": "2.7.1", 25 | "prismjs": "^1.27.0", 26 | "tailwindcss": "^2.1.2", 27 | "vite": "^2.9.13" 28 | }, 29 | "lint-staged": { 30 | "**/*": "prettier --write --ignore-unknown", 31 | "*.{js,css,md}": "prettier --write" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'es5', 3 | tabWidth: 4, 4 | semi: false, 5 | singleQuote: true, 6 | } 7 | -------------------------------------------------------------------------------- /public/.well-known/brave-rewards-verification.txt: -------------------------------------------------------------------------------- 1 | This is a Brave Rewards publisher verification file. 2 | 3 | Domain: flexbox.tech 4 | Token: bbcf71767fa80286bd749b16e07c7a884c982135e1fbb3afc2be36f8531d9bda -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/two-beards/flexbox-playground/312cb36161b9a88f774d19b4e7d59487b5c58975/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 47 | 48 | 69 | -------------------------------------------------------------------------------- /src/assets/main.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | 4 | #carbonads { 5 | @apply font-sans flex max-w-xs bg-white fixed h-40 bottom-0 left-0 px-4 pt-4 border-t border-gray-300 -ml-px; 6 | } 7 | 8 | #carbonads a { 9 | text-decoration: none; 10 | } 11 | 12 | #carbonads span { 13 | @apply relative block overflow-hidden; 14 | } 15 | 16 | #carbonads .carbon-wrap { 17 | @apply flex rounded shadow-md bg-gray-100 text-gray-900; 18 | } 19 | 20 | .carbon-img { 21 | display: block; 22 | margin: 0; 23 | line-height: 1; 24 | } 25 | 26 | .carbon-img img { 27 | display: block; 28 | } 29 | 30 | .carbon-text { 31 | @apply text-xs p-2 leading-normal text-left; 32 | } 33 | 34 | .carbon-poweredby { 35 | @apply block p-2 text-center leading-none font-semibold text-xs text-gray-600 bg-transparent; 36 | } 37 | 38 | @tailwind utilities; 39 | -------------------------------------------------------------------------------- /src/assets/prism.css: -------------------------------------------------------------------------------- 1 | /** 2 | * prism.js theme 3 | */ 4 | 5 | :root { 6 | --inline-code-bg: #f5f3ff; 7 | --inline-code-color: #6d28d9; 8 | --code-border: #cbd5e1; 9 | --code-fg: #6e7f91; 10 | --code-comment: #a8b3bd; 11 | --code-blue: #2563eb; 12 | --code-cyan: #06b6d4; 13 | --code-purple: #7c3aed; 14 | --code-green: #10b981; 15 | --code-red: #ef4444; 16 | --code-yellow: #f59e0b; 17 | --tab-size: 4; 18 | } 19 | 20 | /* purgecss start ignore */ 21 | 22 | code[class*='language-'], 23 | pre.prismjs { 24 | color: var(--code-fg); 25 | background: none; 26 | border-radius: 0; 27 | padding: 0; 28 | font-size: 1rem; 29 | text-align: left; 30 | white-space: pre; 31 | word-spacing: normal; 32 | word-break: normal; 33 | word-wrap: normal; 34 | line-height: 1.5; 35 | 36 | -moz-tab-size: var(--tab-size); 37 | -o-tab-size: var(--tab-size); 38 | tab-size: var(--tab-size); 39 | 40 | -webkit-hyphens: none; 41 | -moz-hyphens: none; 42 | -ms-hyphens: none; 43 | hyphens: none; 44 | } 45 | 46 | /* Code blocks */ 47 | pre.prismjs { 48 | background: transparent; 49 | border-top: 1px solid var(--code-border); 50 | border-bottom: 1px solid var(--code-border); 51 | margin: 1rem -1rem 2rem; 52 | overflow: auto; 53 | padding: 1rem; 54 | width: 100vw; 55 | } 56 | 57 | @media all and (min-width: 640px) { 58 | pre.prismjs { 59 | border-radius: 0.25rem; 60 | border: 1px solid var(--code-border); 61 | margin-left: 0; 62 | margin-right: 0; 63 | width: auto; 64 | } 65 | } 66 | 67 | .token.comment, 68 | .token.block-comment, 69 | .token.prolog, 70 | .token.doctype, 71 | .token.cdata { 72 | color: var(--code-comment); 73 | } 74 | 75 | .token.punctuation { 76 | color: var(--code-fg); 77 | } 78 | 79 | .token.tag, 80 | .token.namespace, 81 | .token.boolean, 82 | .token.deleted, 83 | .token.number, 84 | .token.interpolation-punctuation { 85 | color: var(--code-red); 86 | } 87 | 88 | .token.function-name, 89 | .token.function, 90 | .token.attr-name, 91 | .token.property, 92 | .language-html .token.attr-value, 93 | .token.url { 94 | color: var(--code-blue); 95 | } 96 | 97 | .language-html .token.property, 98 | .language-html .token.attr-name, 99 | .language-html .token.namespace, 100 | .token.class-name, 101 | .token.constant, 102 | .token.variable, 103 | .token.symbol { 104 | color: var(--code-yellow); 105 | } 106 | 107 | .token.selector, 108 | .token.important, 109 | .token.atrule, 110 | .token.keyword, 111 | .token.builtin { 112 | color: var(--code-purple); 113 | } 114 | 115 | .token.operator, 116 | .token.entity { 117 | color: var(--code-cyan); 118 | } 119 | 120 | .token.string, 121 | .token.char, 122 | .token.attr-value, 123 | .token.regex, 124 | .token.inserted { 125 | color: var(--code-green); 126 | } 127 | 128 | .token.important, 129 | .token.bold { 130 | font-weight: bold; 131 | } 132 | 133 | .token.italic { 134 | font-style: italic; 135 | } 136 | 137 | .token.entity { 138 | cursor: help; 139 | } 140 | 141 | /* purgecss end ignore */ 142 | -------------------------------------------------------------------------------- /src/components/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 56 | -------------------------------------------------------------------------------- /src/components/BaseInput.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /src/components/BaseSelect.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 25 | -------------------------------------------------------------------------------- /src/components/CarbonAds.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /src/components/CodeContainer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 50 | -------------------------------------------------------------------------------- /src/components/CssMarkup.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 102 | -------------------------------------------------------------------------------- /src/components/FlexContainer.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 49 | -------------------------------------------------------------------------------- /src/components/FlexItem.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 76 | -------------------------------------------------------------------------------- /src/components/FlexMarkup.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 32 | -------------------------------------------------------------------------------- /src/components/GitHubIcon.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /src/components/HtmlMarkup.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 31 | -------------------------------------------------------------------------------- /src/components/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 34 | -------------------------------------------------------------------------------- /src/components/SidebarContainer.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 101 | -------------------------------------------------------------------------------- /src/components/SidebarItems.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 115 | -------------------------------------------------------------------------------- /src/components/SidebarNav.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 41 | 42 | 57 | -------------------------------------------------------------------------------- /src/components/SidebarNavTab.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 29 | -------------------------------------------------------------------------------- /src/components/Spacer.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/components/TrashCan.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /src/components/TwoBeards.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 55 | 56 | 66 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import store from './store' 4 | import './assets/main.css' 5 | 6 | createApp(App).use(store).mount('#app') 7 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import Vuex from 'vuex' 2 | import { v4 as uuid } from 'uuid' 3 | 4 | const flexProperties = { 5 | flexDirection: ['row', 'row-reverse', 'column', 'column-reverse'], 6 | flexWrap: ['nowrap', 'wrap', 'wrap-reverse'], 7 | justifyContent: [ 8 | 'flex-start', 9 | 'flex-end', 10 | 'center', 11 | 'space-between', 12 | 'space-around', 13 | ], 14 | alignItems: ['flex-start', 'flex-end', 'center', 'stretch', 'baseline'], 15 | alignContent: [ 16 | 'flex-start', 17 | 'flex-end', 18 | 'center', 19 | 'stretch', 20 | 'space-between', 21 | 'space-around', 22 | ], 23 | alignSelf: [ 24 | 'auto', 25 | 'flex-start', 26 | 'flex-end', 27 | 'center', 28 | 'baseline', 29 | 'stretch', 30 | ], 31 | } 32 | 33 | const state = { 34 | flexDirection: flexProperties.flexDirection[0], 35 | flexWrap: flexProperties.flexWrap[0], 36 | justifyContent: flexProperties.justifyContent[0], 37 | alignItems: flexProperties.alignItems[0], 38 | alignContent: flexProperties.alignContent[0], 39 | flexItems: [], 40 | selectedItem: null, 41 | activeTab: 'container', 42 | showFlexMarkup: false, 43 | flexProperties, 44 | } 45 | 46 | export const mutations = { 47 | SET_FLEX_DIRECTION(state, val) { 48 | state.flexDirection = val 49 | }, 50 | SET_FLEX_WRAP(state, val) { 51 | state.flexWrap = val 52 | }, 53 | SET_JUSTIFY_CONTENT(state, val) { 54 | state.justifyContent = val 55 | }, 56 | SET_ALIGN_ITEMS(state, val) { 57 | state.alignItems = val 58 | }, 59 | SET_ALIGN_CONTENT(state, val) { 60 | state.alignContent = val 61 | }, 62 | ADD_FLEX_ITEM(state) { 63 | state.flexItems = [ 64 | ...state.flexItems, 65 | { 66 | id: uuid(), 67 | styles: { 68 | order: 0, 69 | flexGrow: 0, 70 | flexShrink: 1, 71 | flexBasis: 'auto', 72 | alignSelf: 'auto', 73 | }, 74 | }, 75 | ] 76 | }, 77 | REMOVE_SELECTED_ITEM(state) { 78 | const currentItemId = state.selectedItem.id 79 | state.flexItems = state.flexItems.filter( 80 | (it) => it.id !== currentItemId 81 | ) 82 | }, 83 | REMOVE_FLEX_ITEM(state, itemId) { 84 | // if we're removing the currently selected item this way, 85 | // we also need to set the selectedItem to null to avoid errors 86 | if (itemId === state.selectedItem?.id) { 87 | state.selectedItem = null 88 | } 89 | state.flexItems = state.flexItems.filter((it) => it.id !== itemId) 90 | }, 91 | SET_SELECTED_ITEM(state, item) { 92 | if (item === null) { 93 | state.selectedItem = item 94 | } else { 95 | state.selectedItem = state.flexItems.find((it) => it.id === item) 96 | } 97 | }, 98 | UPDATE_SELECTED_ITEM(state, val) { 99 | state.flexItems = state.flexItems.map((it) => { 100 | if (it.id === val.id) { 101 | return val 102 | } 103 | return it 104 | }) 105 | state.selectedItem = val 106 | }, 107 | SET_ACTIVE_TAB(state, val) { 108 | state.activeTab = val 109 | }, 110 | TOGGLE_FLEX_MARKUP(state) { 111 | state.showFlexMarkup = !state.showFlexMarkup 112 | }, 113 | RESET_DEFAULTS(state) { 114 | // reset container properties 115 | state.flexDirection = flexProperties.flexDirection[0] 116 | state.flexWrap = flexProperties.flexWrap[0] 117 | state.justifyContent = flexProperties.justifyContent[0] 118 | state.alignItems = flexProperties.alignItems[0] 119 | state.alignContent = flexProperties.alignContent[0] 120 | 121 | // reset each flex item 122 | state.flexItems.forEach((it) => { 123 | it.styles = { 124 | order: 0, 125 | flexGrow: 0, 126 | flexShrink: 1, 127 | flexBasis: 'auto', 128 | alignSelf: 'auto', 129 | } 130 | }) 131 | }, 132 | } 133 | 134 | export const actions = { 135 | setFlexDirection({ commit }, e) { 136 | commit('SET_FLEX_DIRECTION', e.target.value) 137 | }, 138 | setFlexWrap({ commit }, e) { 139 | commit('SET_FLEX_WRAP', e.target.value) 140 | }, 141 | setJustifyContent({ commit }, e) { 142 | commit('SET_JUSTIFY_CONTENT', e.target.value) 143 | }, 144 | setAlignItems({ commit }, e) { 145 | commit('SET_ALIGN_ITEMS', e.target.value) 146 | }, 147 | setAlignContent({ commit }, e) { 148 | commit('SET_ALIGN_CONTENT', e.target.value) 149 | }, 150 | addFlexItem({ commit }) { 151 | commit('ADD_FLEX_ITEM') 152 | }, 153 | removeSelectedItem({ commit }) { 154 | commit('REMOVE_SELECTED_ITEM') 155 | commit('SET_SELECTED_ITEM', null) 156 | }, 157 | removeFlexItem({ commit }, val) { 158 | commit('REMOVE_FLEX_ITEM', val) 159 | }, 160 | setSelectedItem({ commit }, val) { 161 | commit('SET_SELECTED_ITEM', val) 162 | 163 | if (val !== null) { 164 | commit('SET_ACTIVE_TAB', 'items') 165 | } 166 | }, 167 | updateSelectedItem({ commit }, val) { 168 | commit('UPDATE_SELECTED_ITEM', val) 169 | }, 170 | setActiveTab({ commit }, val) { 171 | commit('SET_ACTIVE_TAB', val) 172 | }, 173 | toggleMarkup({ commit }) { 174 | commit('TOGGLE_FLEX_MARKUP') 175 | }, 176 | resetDefaults({ commit }) { 177 | commit('RESET_DEFAULTS') 178 | }, 179 | } 180 | 181 | export default Vuex.createStore({ 182 | state, 183 | mutations, 184 | actions, 185 | }) 186 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [require('@tailwindcss/forms')], 12 | } 13 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | }) 8 | --------------------------------------------------------------------------------