├── .env.example ├── .github └── workflows │ └── studio.yml ├── .gitignore ├── LICENSE ├── README.md ├── app.config.ts ├── app.vue ├── components ├── OgImage │ ├── CoverImage.vue │ ├── Custom.vue │ ├── Emoji.vue │ └── Simple.vue ├── app │ ├── Footer.vue │ ├── Layout.vue │ ├── Logo.vue │ ├── Navbar.vue │ ├── Renderer.vue │ └── header │ │ ├── Banner.vue │ │ ├── ColorSelector.vue │ │ └── Dot.vue ├── content │ ├── PreRenderTags.vue │ ├── advertisements │ │ ├── FeatureExample.vue │ │ └── SponsorExample.vue │ ├── blog │ │ └── BlogGrid.vue │ ├── directory │ │ ├── DirectoryGrid.vue │ │ └── Search.vue │ ├── general │ │ ├── LinkButton.vue │ │ ├── NewsletterForm.vue │ │ ├── NewsletterWithCTA.vue │ │ └── Tweet.vue │ ├── hero │ │ ├── CenterHero.vue │ │ ├── ImageHero.vue │ │ ├── SimpleLeftHero.vue │ │ └── TwoColumnHero.vue │ ├── prose │ │ └── ProseA.vue │ └── submit │ │ └── TallyForm.vue ├── directory │ ├── EmptyQueryIndicator.vue │ ├── Featured │ │ ├── Recommendation.vue │ │ └── Tag.vue │ ├── Item.vue │ ├── PureGrid.vue │ └── SubmitBox.vue ├── document │ ├── Empty.vue │ ├── NotFound.vue │ └── Prose.vue └── ui │ ├── Button.vue │ ├── Card.vue │ ├── ShadowCard.vue │ ├── Tag.vue │ └── tag │ ├── Grid.vue │ └── Select.vue ├── composables ├── useCurrentPage.ts ├── useDirectory.ts ├── useFeatured.ts ├── useKeyFocus.ts └── useTags.ts ├── content.config.ts ├── content ├── advertise.md ├── blog.md ├── blog │ └── blog-post-1.md ├── dir │ └── starter.md ├── index.md ├── legal │ ├── privacy-policy.md │ └── terms-of-service.md └── submit.md ├── eslint.config.mjs ├── layouts ├── card.vue ├── default.vue ├── thin.vue └── wide.vue ├── middleware └── setLayout.ts ├── nuxt.config.ts ├── nuxt.schema.ts ├── package.json ├── pages ├── [...slug].vue └── tags │ └── [slug].vue ├── pnpm-lock.yaml ├── public ├── favicon.ico ├── logo.png └── test.jpg ├── tailwind.config.ts ├── tsconfig.json ├── types └── Tag.ts └── utils ├── formatString.ts └── setSeo.ts /.env.example: -------------------------------------------------------------------------------- 1 | POSTHOG_PUBLIC_KEY= 2 | POSTHOG_HOST= -------------------------------------------------------------------------------- /.github/workflows/studio.yml: -------------------------------------------------------------------------------- 1 | 2 | name: studio-nuxt-build 3 | run-name: studio nuxt build 4 | 5 | on: 6 | # Runs on pushes targeting the default branch 7 | push: 8 | branches: 9 | - 'main' 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # Add write workflow permissions 15 | permissions: 16 | contents: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Build job 25 | build-and-deploy: 26 | runs-on: ${{ matrix.os }} 27 | defaults: 28 | run: 29 | working-directory: . 30 | 31 | strategy: 32 | matrix: 33 | os: [ubuntu-latest] 34 | node: [20] 35 | 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v4 39 | 40 | - name: Identify package manager 41 | id: pkgman 42 | run: | 43 | cache=`[ -f "./pnpm-lock.yaml" ] && echo "pnpm" || ([ -f "./package-lock.json" ] && echo "npm" || ([ -f "./yarn.lock" ] && echo "yarn" || echo ""))` 44 | package_manager=`[ ! -z "$cache" ] && echo "$cache" || echo "pnpm"` 45 | echo "cache=$cache" >> $GITHUB_OUTPUT 46 | echo "package_manager=$package_manager" >> $GITHUB_OUTPUT 47 | 48 | - uses: pnpm/action-setup@v4 49 | if: ${{ steps.pkgman.outputs.package_manager == 'pnpm' }} 50 | name: Install pnpm 51 | id: pnpm-install 52 | 53 | - uses: actions/setup-node@v4 54 | with: 55 | version: ${{ matrix.node }} 56 | cache: ${{ steps.pkgman.outputs.cache }} 57 | 58 | - name: Install dependencies 59 | run: ${{ steps.pkgman.outputs.package_manager }} install 60 | 61 | - name: Generate 62 | run: npx nuxi build --preset github_pages 63 | env: 64 | NUXT_CONTENT_PREVIEW_API: https://api.nuxt.studio 65 | 66 | 67 | # Deployment job 68 | - name: Deploy 🚀 69 | uses: JamesIves/github-pages-deploy-action@v4 70 | with: 71 | folder: ./.output/public 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mark Bruderer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Minted Directory Template

3 |

Markdown driven directory template. Built with Nuxt, Nuxt Content and Tailwindcss. Optimized for SEO. Beautiful Customizable Style

4 |
5 | 6 |
7 | 8 |
9 | Minted Directory Screenshot 10 |
11 | 12 |
13 | 14 | Learn more at [minteddirectory.com](https://minteddirectory.com) 15 | 16 | Read the [docs](https://minteddirectory.com/docs) 17 | 18 | ### Features: 19 | + 🖌️ Add Listings with markdown. 20 | + 🔋 Batteries included for SEO (nuxt seo module). 21 | + 💻 Pre-built components for directories. 22 | + 💅 Customizable style. 23 | + 🌙 Dark/Light mode 24 | + 💸 Sponsored Content 25 | 26 | ## Getting Started 27 | 28 | ### Local Development 29 | 30 | Duplicate the template then clone the repository. 31 | 32 | ```sh 33 | git clone git@github.com:youraccount/projectname.git my-directory 34 | ``` 35 | 36 | Or use the github cli to create a repository based on the template and clone in one command: 37 | 38 | ```sh 39 | gh repo create my-directory --template masterkram/minted-directory --private --clone 40 | ``` 41 | 42 | Go to the cloned folder: 43 | ```sh 44 | cd my-directory 45 | ``` 46 | 47 | Install dependencies 48 | 49 | ```sh 50 | pnpm install 51 | ``` 52 | 53 | Run the website: 54 | 55 | ```sh 56 | pnpm dev 57 | ``` 58 | 59 | Congrats :tada: 60 | 61 | You can start customizing and building your directory. 62 | 63 | ### Customization 64 | 65 | To customize the directory style: 66 | + Change the `primary`, `secondary` color and `fontFamily` in `tailwind.config.ts` 67 | + Customize the `app.config.ts` 68 | 69 | Read about the possible changes to the app config [here](https://minteddirectory/docs/settings). 70 | 71 | ### Adding Content 72 | 73 | Add listings by adding markdown files to `/content/dir` 74 | 75 | Add blog articles by adding markdown files to `/content/blog` 76 | 77 | ### Deployment 78 | 79 | Deploy as a pre-rendered, static site for best SEO performance: 80 | 81 | ```bash 82 | pnpm run generate 83 | ``` 84 | 85 | Check out the [deployment documentation](https://minteddirectory.com/docs/deployment) for more information. 86 | 87 | ## Community 88 | 89 | [Join the discord](https://discord.gg/5UbrTNzX7y) 90 | -------------------------------------------------------------------------------- /app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | general: { 3 | title: 'Minted Directory', 4 | logo: '', 5 | iconLogo: 'fluent-emoji-flat:leaf-fluttering-in-wind', 6 | language: 'en', 7 | }, 8 | site: { 9 | // override the general settings for seo tags. 10 | // leave empty for general priority. 11 | // url is necessary for correct function of seo module. 12 | name: 'Minted Directory', 13 | description: 'Example Description', 14 | url: 'https://example.com', 15 | favicon: { 16 | image: '', 17 | emoji: '🍃', 18 | }, 19 | }, 20 | directory: { 21 | listingPageLayout: 'card', 22 | search: { 23 | placeholder: 'Search among {0} tools', 24 | icon: 'tabler:bow', 25 | tags: { 26 | // options: none,select,show-all, 27 | display: 'select', 28 | intersection: false, 29 | }, 30 | }, 31 | grid: { 32 | list: false, 33 | emptyState: { 34 | text: 'Seems that this entry is missing from the archives.', 35 | // options: button, simple, link 36 | type: 'button', 37 | icon: 'tabler:exclamation-mark', 38 | }, 39 | card: { 40 | image: true, 41 | // options: dashed, shadow, outline, bullet 42 | type: 'shadow', 43 | }, 44 | submit: { 45 | show: true, 46 | first: false, 47 | title: 'Submit a template', 48 | description: 49 | 'Submit a template to show off a good project to other people.', 50 | hideable: true, 51 | }, 52 | }, 53 | featured: { 54 | showOnAllPages: true, 55 | showOnSide: true, 56 | icon: 'tabler:star', 57 | labelForCard: 'Featured ✨', 58 | }, 59 | tags: [ 60 | { name: 'SAAS', color: 'blue' }, 61 | { name: 'dashboard', color: 'green' }, 62 | { name: 'landing-page' }, 63 | { name: 'toolbox' }, 64 | { name: 'agency' }, 65 | { name: 'markdown-based' }, 66 | { name: 'basics', color: 'indigo' }, 67 | ], 68 | tagPages: { 69 | title: 'Available {0} products:', 70 | description: 71 | 'View all available tools and templates in the {0} category...', 72 | }, 73 | }, 74 | header: { 75 | banner: { 76 | show: true, 77 | text: 'Create your own directory website in minutes.', 78 | link: 'https://minteddirectory.com', 79 | brandText: 'MintedDirectory', 80 | }, 81 | navbar: { 82 | colorModeSelector: true, 83 | links: [ 84 | { name: 'Directory', to: '/' }, 85 | { name: 'Blog', to: '/blog' }, 86 | { name: 'Advertise', to: '/advertise' }, 87 | { 88 | name: 'Analytics', 89 | to: 'https://us.posthog.com/shared/7dgSk4cvgNYnJwBu6R47kZXHBUBJWQ', 90 | target: '_blank', 91 | }, 92 | ], 93 | }, 94 | actionButton: { 95 | text: 'Submit a listing', 96 | href: '/submit', 97 | }, 98 | }, 99 | footer: { 100 | description: "Best directory for my niche.", 101 | navigation: [ 102 | { 103 | title: "Directory", links: [{ title: "Submit", link: "/submit" }, { title: "Advertise", link: "/advertise" }], 104 | }, 105 | { 106 | title: "Categories", links: [ 107 | { title: "SAAS", link: "/tags/saas" }, 108 | { title: "Dashboard", link: "/tags/dashboard" }, 109 | { title: "Landing Page", link: "/tags/landing-page" }, 110 | { title: "Toolbox", link: "/tags/toolbox" }, 111 | ], 112 | }, 113 | { 114 | title: "Blog", links: [{ title: "Articles", link: "/blog" }], 115 | }, 116 | { 117 | title: "Legal", links: [{ title: "Privacy Policy", link: "/legal/terms-of-service" }, { title: "Terms of Service", link: "/legal/privacy-policy" }], 118 | }, 119 | ], 120 | socials: { 121 | github: { 122 | link: '', 123 | icon: 'tabler:brand-github', 124 | }, 125 | facebook: { 126 | link: '', 127 | icon: 'tabler:brand-facebook', 128 | }, 129 | instagram: { 130 | link: '', 131 | icon: 'tabler:brand-instagram', 132 | }, 133 | x: { 134 | link: 'https://x.com/mark_bruderer', 135 | icon: 'tabler:brand-twitter', 136 | }, 137 | youtube: { 138 | link: 'https://www.youtube.com/@mark_hacks', 139 | icon: 'tabler:brand-youtube', 140 | }, 141 | }, 142 | }, 143 | ui: { 144 | icons: { 145 | dark: 'tabler:moon', 146 | light: 'tabler:sun', 147 | }, 148 | }, 149 | }); 150 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | 27 | 63 | -------------------------------------------------------------------------------- /components/OgImage/CoverImage.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /components/OgImage/Custom.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 44 | -------------------------------------------------------------------------------- /components/OgImage/Emoji.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /components/OgImage/Simple.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/app/Footer.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 94 | -------------------------------------------------------------------------------- /components/app/Layout.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /components/app/Logo.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 26 | -------------------------------------------------------------------------------- /components/app/Navbar.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 130 | -------------------------------------------------------------------------------- /components/app/Renderer.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 19 | -------------------------------------------------------------------------------- /components/app/header/Banner.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 63 | -------------------------------------------------------------------------------- /components/app/header/ColorSelector.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | -------------------------------------------------------------------------------- /components/app/header/Dot.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /components/content/PreRenderTags.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /components/content/advertisements/FeatureExample.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /components/content/advertisements/SponsorExample.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /components/content/blog/BlogGrid.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /components/content/directory/DirectoryGrid.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 38 | -------------------------------------------------------------------------------- /components/content/directory/Search.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 52 | -------------------------------------------------------------------------------- /components/content/general/LinkButton.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /components/content/general/NewsletterForm.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/content/general/NewsletterWithCTA.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/content/general/Tweet.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 48 | 49 | -------------------------------------------------------------------------------- /components/content/hero/CenterHero.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | -------------------------------------------------------------------------------- /components/content/hero/ImageHero.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 45 | -------------------------------------------------------------------------------- /components/content/hero/SimpleLeftHero.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /components/content/hero/TwoColumnHero.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 32 | 33 | 42 | -------------------------------------------------------------------------------- /components/content/prose/ProseA.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /components/content/submit/TallyForm.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 |