├── .github ├── images │ ├── Tailwind_CSS_Logo.svg.png │ ├── alpine-icon.png │ ├── astro-icon.png │ ├── gsap.png │ ├── image.png │ ├── pagespeed-small.png │ ├── screenshot.png │ └── tailwind-icon.png └── workflows │ ├── .gitkeep │ └── unlighthouse.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tailwind.json ├── README.md ├── astro.config.mjs ├── bun.lock ├── bunfig.toml ├── cspell.config.yaml ├── package.json ├── public ├── apple-touch-icon-180x180.png ├── favicon.ico ├── favicon.svg ├── fonts │ └── Satoshi-Variable.woff2 ├── logo.svg ├── manifest.webmanifest ├── maskable-icon-512x512.png ├── maskable-icon.png ├── projects │ ├── astroagency │ │ └── astroagency.webm │ ├── astros │ │ └── astros.webm │ ├── astroshop │ │ └── astroshop.webm │ ├── comingsoon │ │ └── comingsoon.webm │ ├── dreamteam │ │ └── dreamteam-landingpage.webm │ └── flexfolio │ │ └── flexfolio.webm ├── pwa-192x192.png ├── pwa-512x512.png ├── pwa-64x64.png └── robots.txt ├── src ├── assets │ └── images │ │ ├── authors │ │ └── charlie-foster.png │ │ ├── blog │ │ ├── ai-webdev-2024.jpg │ │ ├── edge-computing-2024.jpg │ │ ├── mobile-development-trends-2024.jpg │ │ └── web-development-trends-2024.jpg │ │ ├── global │ │ ├── logo.svg │ │ └── logoFull.svg │ │ ├── projects │ │ ├── dreamteamdesign.png │ │ └── yourProject.png │ │ └── services │ │ ├── advertising.svg │ │ ├── creative-designer-doing-multitasking.svg │ │ ├── mobile-app-ui-ux-design.svg │ │ ├── seo-link-building.svg │ │ └── website-ui-ux-development.svg ├── components │ ├── BaseHead.astro │ ├── blog │ │ ├── Articles.astro │ │ ├── Author.astro │ │ ├── Authors.astro │ │ ├── Comments.astro │ │ ├── Cta.astro │ │ ├── Entry.astro │ │ ├── Newsletter.astro │ │ ├── Tag.astro │ │ └── Tags.astro │ ├── forms │ │ └── Contact.astro │ ├── global │ │ ├── BackgroundLines.astro │ │ ├── BlobAnimation.astro │ │ ├── Footer.astro │ │ ├── GrainyGradient.astro │ │ ├── LinkBlend.astro │ │ ├── Navigation.astro │ │ └── Title.astro │ ├── infopages │ │ ├── Error404.astro │ │ ├── Faq.astro │ │ ├── Privacy.astro │ │ ├── Terms.astro │ │ └── ThankYou.astro │ ├── landing │ │ ├── AllInOne.astro │ │ ├── Cta.astro │ │ ├── Description.astro │ │ ├── Facts.astro │ │ ├── Faq.astro │ │ ├── Hero.astro │ │ ├── Services.astro │ │ ├── Tagline.astro │ │ ├── Testimonials.astro │ │ └── WorkPreview.astro │ ├── services │ │ └── ServicesMain.astro │ └── work │ │ ├── CaseStudy.astro │ │ └── WokCard.astro ├── content │ ├── authors │ │ ├── en │ │ │ └── charlie-foster.md │ │ └── it │ │ │ └── charlie-foster.md │ ├── config.ts │ ├── posts │ │ ├── en │ │ │ ├── ai-webdev-2024.mdx │ │ │ ├── edge-computing-2025.mdx │ │ │ ├── game-changing-mobile-development-trends-in-2025.mdx │ │ │ └── web-development-trends-2025.mdx │ │ └── it │ │ │ ├── ai-webdev-2024.mdx │ │ │ ├── edge-computing-2024.mdx │ │ │ ├── game-changing-mobile-development-trends-in-2024.mdx │ │ │ └── web-development-trends-2024.mdx │ └── projects │ │ ├── en │ │ ├── astroagency.mdx │ │ ├── astros.mdx │ │ ├── comingsoon.1.mdx │ │ ├── comingsoon.mdx │ │ └── flexfolio.mdx │ │ └── it │ │ ├── astroagency.mdx │ │ ├── astros.mdx │ │ ├── comingsoon.1.mdx │ │ ├── comingsoon.mdx │ │ └── flexfolio.mdx ├── env.d.ts ├── i18n │ ├── ui.ts │ └── utils.ts ├── layouts │ ├── BaseLayout.astro │ └── MarkdownPostLayout.astro ├── pages │ ├── 404.astro │ ├── author │ │ ├── [author].astro │ │ └── index.astro │ ├── blog │ │ ├── [...slug].astro │ │ └── index.astro │ ├── contact.astro │ ├── index.astro │ ├── it │ │ ├── 404.astro │ │ ├── author │ │ │ ├── [author].astro │ │ │ └── index.astro │ │ ├── blog │ │ │ ├── [...slug].astro │ │ │ └── index.astro │ │ ├── contact.astro │ │ ├── index.astro │ │ ├── privacy.astro │ │ ├── services │ │ │ └── index.astro │ │ ├── tags │ │ │ ├── [tag].astro │ │ │ └── index.astro │ │ ├── terms.astro │ │ ├── thank-you.astro │ │ └── work │ │ │ └── [...slug].astro │ ├── privacy.astro │ ├── rss.xml.ts │ ├── services │ │ └── index.astro │ ├── tags │ │ ├── [tag].astro │ │ └── index.astro │ ├── terms.astro │ ├── thank-you.astro │ └── work │ │ └── [...slug].astro └── styles │ └── global.css ├── tailwind.config.cjs ├── tsconfig.json └── unlighthouse.config.ts /.github/images/Tailwind_CSS_Logo.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/Tailwind_CSS_Logo.svg.png -------------------------------------------------------------------------------- /.github/images/alpine-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/alpine-icon.png -------------------------------------------------------------------------------- /.github/images/astro-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/astro-icon.png -------------------------------------------------------------------------------- /.github/images/gsap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/gsap.png -------------------------------------------------------------------------------- /.github/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/image.png -------------------------------------------------------------------------------- /.github/images/pagespeed-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/pagespeed-small.png -------------------------------------------------------------------------------- /.github/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/screenshot.png -------------------------------------------------------------------------------- /.github/images/tailwind-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/images/tailwind-icon.png -------------------------------------------------------------------------------- /.github/workflows/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/.github/workflows/.gitkeep -------------------------------------------------------------------------------- /.github/workflows/unlighthouse.yml: -------------------------------------------------------------------------------- 1 | name: Assertions and static report 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | env: 10 | PRIVJS_TOKEN: ${{ secrets.PRIVJS_TOKEN }} 11 | 12 | jobs: 13 | test-and-deploy: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 10 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - uses: oven-sh/setup-bun@v1 20 | with: 21 | bun-version: latest 22 | 23 | - name: Install dependencies 24 | run: bun install 25 | 26 | - name: Install Global dependencies 27 | run: bun install -g netlify-cli @unlighthouse/cli puppeteer 28 | 29 | - name: Unlighthouse assertions and client 30 | run: unlighthouse-ci 31 | 32 | - name: Deploy 33 | run: netlify deploy --dir=.unlighthouse --prod --message="New Release Deploy from GitHub Actions" 34 | env: 35 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 36 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # dependencies 5 | node_modules/ 6 | 7 | # logs 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | pnpm-debug.log* 12 | 13 | 14 | # environment variables 15 | .env 16 | .env.production 17 | 18 | # macOS-specific files 19 | .DS_Store 20 | 21 | .astro 22 | 23 | .unlighthouse 24 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | always-auth=true 2 | //npm.greensock.com/:_authToken=${GSAP_TOKEN} 3 | @gsap:registry=https://npm.greensock.com 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "astro-build.astro-vscode", 4 | "bradlc.vscode-tailwindcss", 5 | "christian-kohler.path-intellisense", 6 | "adrianwilczynski.alpine-js-intellisense" 7 | ], 8 | "unwantedRecommendations": [] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "call .\\node_modules\\.bin\\astro dev", 6 | "name": "server", 7 | "request": "launch", 8 | "type": "node-terminal", 9 | "internalConsoleOptions": "neverOpen" 10 | }, 11 | { 12 | "type": "chrome", 13 | "request": "launch", 14 | "name": "client", 15 | "url": "http://localhost:4321", 16 | "webRoot": "${workspaceFolder}/src", 17 | "sourceMapPathOverrides": { 18 | "vite:///src/*": "${webRoot}/*" 19 | }, 20 | "skipFiles": [ 21 | "**/node_modules/**" 22 | ] 23 | } 24 | ], 25 | "compounds": [ 26 | { 27 | "name": "Debug Server and Client", 28 | "configurations": [ 29 | "server", 30 | "client" 31 | ] 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "css.customData": [".vscode/tailwind.json"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/tailwind.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1.1, 3 | "atDirectives": [ 4 | { 5 | "name": "@tailwind", 6 | "description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.", 7 | "references": [ 8 | { 9 | "name": "Tailwind Documentation", 10 | "url": "https://tailwindcss.com/docs/functions-and-directives#tailwind" 11 | } 12 | ] 13 | }, 14 | { 15 | "name": "@apply", 16 | "description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.", 17 | "references": [ 18 | { 19 | "name": "Tailwind Documentation", 20 | "url": "https://tailwindcss.com/docs/functions-and-directives#apply" 21 | } 22 | ] 23 | }, 24 | { 25 | "name": "@responsive", 26 | "description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n", 27 | "references": [ 28 | { 29 | "name": "Tailwind Documentation", 30 | "url": "https://tailwindcss.com/docs/functions-and-directives#responsive" 31 | } 32 | ] 33 | }, 34 | { 35 | "name": "@screen", 36 | "description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n", 37 | "references": [ 38 | { 39 | "name": "Tailwind Documentation", 40 | "url": "https://tailwindcss.com/docs/functions-and-directives#screen" 41 | } 42 | ] 43 | }, 44 | { 45 | "name": "@variants", 46 | "description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n", 47 | "references": [ 48 | { 49 | "name": "Tailwind Documentation", 50 | "url": "https://tailwindcss.com/docs/functions-and-directives#variants" 51 | } 52 | ] 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Fomalhaut](https://fomalhaut.majestico.co) 2 | 3 | > [!IMPORTANT] 4 | > Currently this theme only works with the premium version of GSAP, the steps to use the standard version will be available in the future. 5 | 6 | This theme takes inspiration from the Framer theme [formstudio](https://formstudio.site/) 7 | 8 | Instruction to change the main title animation will be available in the future. 9 | 10 | ![Astro](.github/images/astro-icon.png) 11 | ![Tailwind](.github/images/tailwind-icon.png) 12 | ![Gsap](.github/images/gsap.png) 13 | 14 | Fomalhaut is a template made with [Astro](https://astro.build), [Tailwind](https://tailwindcss.com/) and [Gsap](https://gsap.com/) 15 | 16 | [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/majesticooss/fomalhaut) 17 | 18 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/majesticooss/fomalhaut) 19 | 20 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/majesticooss/fomalhaut) 21 | 22 | 23 | ### [🧪 Site preview →](https://fomalhaut.majestico.co) 24 | 25 | ### [🧑‍🚀 Astro website →](https://astro.build/) 26 | 27 | ### [🕮 Astro docs →](https://docs.astro.build/en/getting-started/) 28 | 29 | --- 30 | 31 | ## Preview 32 | 33 | ![Fomalhaut Preview](.github/images/screenshot.png) 34 | 35 | ## 🧪 Test 36 | 37 | On the folder run 38 | 39 | 1. `bun install` (or `yarn` or `pnpm i`) 40 | 2. `bun run dev` (or `yarn dev` or `pnpm dev`) 41 | 42 | ## ✅ Features 43 | 44 | - [x] Localization 45 | - [x] Blog 46 | 47 | --- 48 | 49 |

majestico.co

50 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import mdx from "@astrojs/mdx"; 2 | import sitemap from "@astrojs/sitemap"; 3 | import tailwind from "@astrojs/tailwind"; 4 | import icon from "astro-icon"; 5 | import { defineConfig } from "astro/config"; 6 | 7 | // https://astro.build/config 8 | export default defineConfig({ 9 | vite: { 10 | server: { 11 | watch: { 12 | usePolling: true, 13 | }, 14 | }, 15 | }, 16 | site: "https://majestico.co", 17 | i18n: { 18 | defaultLocale: "en", 19 | locales: ["en", "it"], 20 | }, 21 | markdown: { 22 | drafts: true, 23 | shikiConfig: { 24 | theme: "css-variables", 25 | }, 26 | }, 27 | shikiConfig: { 28 | wrap: true, 29 | skipInline: false, 30 | drafts: true, 31 | }, 32 | integrations: [ 33 | tailwind({ 34 | applyBaseStyles: false, 35 | }), 36 | sitemap(), 37 | mdx(), 38 | icon(), 39 | ], 40 | }); 41 | -------------------------------------------------------------------------------- /bunfig.toml: -------------------------------------------------------------------------------- 1 | [install] 2 | # Set default registry 3 | registry = "https://registry.npmjs.org" 4 | 5 | # Configure private registry scoped to a particular organization 6 | [install.scopes] 7 | "@gsap" = { url = "https://npm.greensock.com", token = "$GSAP_TOKEN" } 8 | -------------------------------------------------------------------------------- /cspell.config.yaml: -------------------------------------------------------------------------------- 1 | version: "0.2" 2 | language: en 3 | dictionaries: 4 | - css 5 | - html 6 | - typescript 7 | enableFiletypes: 8 | - ignore 9 | - properties 10 | - astro 11 | - xml 12 | files: 13 | - "**/.*" 14 | - "**/*.{astro,css,js,json,jsonc,html,md,scss,svelte,svg,yaml,yml,xml}" 15 | useGitignore: true 16 | ignorePaths: 17 | - pnpm-lock.yaml 18 | - data/**/*.json 19 | - dist/** 20 | words: 21 | - astrolib 22 | - logoclouds 23 | - majestico 24 | - Satoshi 25 | - squircle 26 | overrides: 27 | - language: en-gb 28 | filename: "**/*.md" 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@majesticostudio/majestico.co", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "private": true, 6 | "description": "majestico.co website", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/majesticostudio/majestico.co.git" 10 | }, 11 | "bugs": "https://github.com/majesticostudio/majestico.co/issues", 12 | "homepage": "https://majestico.co", 13 | "files": [ 14 | "*" 15 | ], 16 | "scripts": { 17 | "dev": "astro dev", 18 | "start": "astro dev", 19 | "build": "astro build", 20 | "preview": "astro preview", 21 | "astro": "astro", 22 | "pwa:generate-assets": "pwa-assets-generator --preset minimal public/logo.svg" 23 | }, 24 | "dependencies": { 25 | "@astrojs/mdx": "4.0.8", 26 | "@astrojs/rss": "4.0.11", 27 | "@astrojs/sitemap": "3.2.1", 28 | "@astrojs/tailwind": "6.0.0", 29 | "astro": "5.3.0", 30 | "gsap": "npm:@gsap/business@3.12.5", 31 | "tailwindcss": "3.4.3" 32 | }, 33 | "devDependencies": { 34 | "@astrolib/seo": "1.0.0-beta.8", 35 | "@cloudflare/workers-types": "4.20250214.0", 36 | "@iconify-json/ic": "1.2.2", 37 | "@iconify-json/iconamoon": "1.2.2", 38 | "@iconify-json/octicon": "1.2.5", 39 | "@tailwindcss/aspect-ratio": "0.4.2", 40 | "@tailwindcss/forms": "0.5.10", 41 | "@tailwindcss/typography": "0.5.16", 42 | "@vite-pwa/assets-generator": "0.2.6", 43 | "astro-icon": "1.1.5", 44 | "prettier": "3.5.2", 45 | "prettier-plugin-astro": "0.14.1", 46 | "prettier-plugin-tailwindcss": "0.6.11", 47 | "unlighthouse": "0.15.0" 48 | }, 49 | "prettier": { 50 | "tabWidth": 4, 51 | "useTabs": true, 52 | "bracketSameLine": true, 53 | "printWidth": 150, 54 | "htmlWhitespaceSensitivity": "ignore", 55 | "plugins": [ 56 | "prettier-plugin-astro", 57 | "prettier-plugin-tailwindcss" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /public/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /public/fonts/Satoshi-Variable.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/fonts/Satoshi-Variable.woff2 -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /public/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "theme_color": "#000000", 3 | "background_color": "#ffffff", 4 | "display": "standalone", 5 | "scope": "/", 6 | "start_url": "/", 7 | "description": "Web Agency Leading in Performant Web Design Solutions - Majestico Studio", 8 | "name": "Majestico", 9 | "short_name": "Majestico", 10 | "icons": [ 11 | { 12 | "src": "/pwa-64x64.png", 13 | "sizes": "64x64", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "/pwa-192x192.png", 18 | "sizes": "192x192", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "/pwa-512x512.png", 23 | "sizes": "512x512", 24 | "type": "image/png" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /public/maskable-icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/maskable-icon-512x512.png -------------------------------------------------------------------------------- /public/maskable-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/maskable-icon.png -------------------------------------------------------------------------------- /public/projects/astroagency/astroagency.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/projects/astroagency/astroagency.webm -------------------------------------------------------------------------------- /public/projects/astros/astros.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/projects/astros/astros.webm -------------------------------------------------------------------------------- /public/projects/astroshop/astroshop.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/projects/astroshop/astroshop.webm -------------------------------------------------------------------------------- /public/projects/comingsoon/comingsoon.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/projects/comingsoon/comingsoon.webm -------------------------------------------------------------------------------- /public/projects/dreamteam/dreamteam-landingpage.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/projects/dreamteam/dreamteam-landingpage.webm -------------------------------------------------------------------------------- /public/projects/flexfolio/flexfolio.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/projects/flexfolio/flexfolio.webm -------------------------------------------------------------------------------- /public/pwa-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/pwa-192x192.png -------------------------------------------------------------------------------- /public/pwa-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/pwa-512x512.png -------------------------------------------------------------------------------- /public/pwa-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/public/pwa-64x64.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | 4 | Sitemap: https://majestico.co/sitemap-index.xml 5 | -------------------------------------------------------------------------------- /src/assets/images/authors/charlie-foster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/src/assets/images/authors/charlie-foster.png -------------------------------------------------------------------------------- /src/assets/images/blog/ai-webdev-2024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/src/assets/images/blog/ai-webdev-2024.jpg -------------------------------------------------------------------------------- /src/assets/images/blog/edge-computing-2024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/src/assets/images/blog/edge-computing-2024.jpg -------------------------------------------------------------------------------- /src/assets/images/blog/mobile-development-trends-2024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/src/assets/images/blog/mobile-development-trends-2024.jpg -------------------------------------------------------------------------------- /src/assets/images/blog/web-development-trends-2024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/src/assets/images/blog/web-development-trends-2024.jpg -------------------------------------------------------------------------------- /src/assets/images/global/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /src/assets/images/global/logoFull.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 14 | 20 | 21 | 29 | 33 | 37 | 42 | 47 | 49 | 52 | 56 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/assets/images/projects/dreamteamdesign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/src/assets/images/projects/dreamteamdesign.png -------------------------------------------------------------------------------- /src/assets/images/projects/yourProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majesticooss/fomalhaut/8248fdbf602ad27c013655f5b34d242596f031b3/src/assets/images/projects/yourProject.png -------------------------------------------------------------------------------- /src/components/BaseHead.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { AstroSeo, type AstroSeoProps } from "@astrolib/seo"; 3 | import "../styles/global.css"; 4 | 5 | type Props = { 6 | seo?: AstroSeoProps; 7 | }; 8 | 9 | const { seo } = Astro.props; 10 | --- 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 59 | -------------------------------------------------------------------------------- /src/components/blog/Articles.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import EntriesOne from "@/components/blog/Entry.astro"; 4 | import Title from "@components/global/Title.astro"; 5 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 6 | 7 | const lang = getLangFromUrl(Astro.url); 8 | const t = useTranslations(lang); 9 | const translatePath = useTranslatedPath(lang); 10 | 11 | const allPosts = (await getCollection("posts")) 12 | .filter((post) => { 13 | const [postLang, ...slug] = post.slug.split("/"); 14 | return postLang === lang; 15 | }) 16 | .map((post) => { 17 | const [postLang, slug] = post.slug.split("/"); 18 | 19 | return { 20 | ...post, 21 | slug: slug, 22 | }; 23 | }) 24 | .sort((a, b) => Number(b.data.pubDate) - Number(a.data.pubDate)); 25 | --- 26 | 27 |
28 |
29 |
30 | 31 | 36 | 37 | { 38 | allPosts && allPosts[0] && ( 39 | <EntriesOne 40 | url={"/blog/" + allPosts[0].slug} 41 | title={allPosts[0].data.title} 42 | description={allPosts[0].data.description} 43 | alt={allPosts[0].data.title} 44 | pubDate={allPosts[0].data.pubDate.toString().slice(0, 10)} 45 | author={allPosts[0].data.author} 46 | image={allPosts[0].data.image.source} 47 | /> 48 | ) 49 | } 50 | 51 | <div class="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-3"> 52 | { 53 | allPosts 54 | .slice(1) 55 | .map((post) => ( 56 | <EntriesOne 57 | url={"/blog/" + post.slug} 58 | title={post.data.title} 59 | description={post.data.description} 60 | alt={post.data.title} 61 | pubDate={post.data.pubDate.toString().slice(0, 10)} 62 | author={post.data.author} 63 | image={post.data.image.source} 64 | /> 65 | )) 66 | } 67 | </div> 68 | </div> 69 | </div> 70 | </section> 71 | 72 | <style> 73 | .article-overlay { 74 | background-repeat: no-repeat; 75 | background-position: center; 76 | background-size: cover; 77 | } 78 | </style> 79 | -------------------------------------------------------------------------------- /src/components/blog/Author.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "@components/global/Title.astro"; 3 | 4 | const { author } = Astro.props; 5 | --- 6 | 7 | <section> 8 | <div class="mx-auto px-8 py-24 md:px-12 lg:pt-32 2xl:max-w-7xl"> 9 | <div class="py-10"> 10 | <Title title={author.data.name} subtitle="author" class="pb-10 pt-20 uppercase" /> 11 | </div> 12 | </div> 13 | </section> 14 | -------------------------------------------------------------------------------- /src/components/blog/Authors.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import { Icon } from "astro-icon/components"; 4 | import Title from "@components/global/Title.astro"; 5 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 6 | 7 | const lang = getLangFromUrl(Astro.url); 8 | const t = useTranslations(lang); 9 | const translatePath = useTranslatedPath(lang); 10 | 11 | const allAuthors = (await getCollection("authors")) 12 | .filter((author) => { 13 | const [authorLang, ...slug] = author.slug.split("/"); 14 | return authorLang === lang; 15 | }) 16 | .map((author) => { 17 | const [authorLang, slug] = author.slug.split("/"); 18 | 19 | return { 20 | ...author, 21 | slug: slug, 22 | }; 23 | }); 24 | --- 25 | 26 | <section> 27 | <div class="mx-auto px-8 py-24 md:px-12 lg:pt-32 2xl:max-w-7xl"> 28 | <div> 29 | <Title class="pb-10 pt-20 uppercase" title="Authors" subtitle="lorem ipsum" /> 30 | </div> 31 | <div> 32 | <ol class="mt-12 flex flex-col divide-y divide-zinc-800 py-8"> 33 | { 34 | allAuthors.map((author) => ( 35 | <li class="w-full py-8 font-mono text-xl font-semibold text-zinc-800 hover:text-black md:text-2xl "> 36 | <a href={translatePath(`/author/${author.slug}`)} class="flex w-full items-center justify-between"> 37 | {author.data.name}{" "} 38 | <span class="ml-auto"> 39 | <Icon name="ic:baseline-arrow-right" width="30" /> 40 | </span> 41 | </a> 42 | </li> 43 | )) 44 | } 45 | </ol> 46 | </div> 47 | </div> 48 | </section> 49 | -------------------------------------------------------------------------------- /src/components/blog/Comments.astro: -------------------------------------------------------------------------------- 1 | --- 2 | /* Generate Giscus from 3 | * https://giscus.app/ 4 | * you only need to change the following fields: 5 | * dataset.repo="XXX" 6 | * dataset.repoId="XXX" 7 | * dataset.category="XXX" 8 | * dataset.categoryId="XXX" 9 | **/ 10 | --- 11 | 12 | <div 13 | class="prose prose-lg mx-auto pt-20 dark:prose-invert" 14 | data-xdata={`{ 15 | initGiscus: function() { 16 | let script = document.createElement('script'); 17 | script.src = 'https://giscus.app/client.js'; 18 | script.dataset.repo = 'majesticostudio/majesticostudio'; 19 | script.dataset.repoId = 'R_kgDOK_K6WQ'; 20 | script.dataset.category = 'Blog'; 21 | script.dataset.categoryId = 'DIC_kwDOK_K6Wc4CcGH6'; 22 | script.dataset.mapping = 'og:title'; 23 | script.dataset.strict = '0'; 24 | script.dataset.reactionsEnabled = '1'; 25 | script.dataset.emitMetadata = '0'; 26 | script.dataset.inputPosition = 'top'; 27 | script.dataset.theme = localStorage.theme == 'dark' ? 'dark' : 'light'; 28 | script.dataset.lang = 'en'; 29 | script.dataset.loading = 'lazy'; 30 | script.crossOrigin = 'anonymous'; 31 | script.async = true; 32 | this.$el.appendChild(script); 33 | } 34 | }`} 35 | x-init="initGiscus()"> 36 | </div> 37 | -------------------------------------------------------------------------------- /src/components/blog/Cta.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Newsletter from "@components/blog/Newsletter.astro"; 3 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 4 | 5 | const lang = getLangFromUrl(Astro.url); 6 | const t = useTranslations(lang); 7 | const translatePath = useTranslatedPath(lang); 8 | 9 | interface Props { 10 | class?: string; 11 | } 12 | 13 | const { class: className } = Astro.props; 14 | --- 15 | 16 | <section class:list={["bg-white text-black", className]}> 17 | <div class="mx-auto max-w-7xl px-8 py-12 md:px-12 lg:px-32 lg:py-24"> 18 | <div class="flex flex-col items-center justify-center gap-10 p-8 text-center lg:p-20"> 19 | <p class="mt-8 font-display text-4xl font-semibold md:text-6xl lg:text-8xl">{t("newsletter_subscribe")}</p> 20 | <Newsletter class="w-full" /> 21 | </div> 22 | </div> 23 | </section> 24 | -------------------------------------------------------------------------------- /src/components/blog/Entry.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from "astro:assets"; 3 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 4 | 5 | const lang = getLangFromUrl(Astro.url); 6 | const t = useTranslations(lang); 7 | const translatePath = useTranslatedPath(lang); 8 | 9 | const { title, url, author, pubDate, description, image, titleInside = false } = Astro.props; 10 | --- 11 | 12 | <a href={translatePath(url)} title={title} class="group"> 13 | <article class="relative z-20 flex h-full flex-1 flex-col overflow-hidden"> 14 | <div class:list=" 15 | article-overlay relative block w-full overflow-hidden lg:col-span-2 article-gradient-overlay"> 16 | <Image class="relative aspect-[384/246] h-full bg-center object-cover" src={image} alt={title} /> 17 | </div> 18 | <div class="flex w-full flex-1 flex-col items-start justify-between pt-4 text-white md:pt-6"> 19 | <div class="w-full"> 20 | <div> 21 | <p class="font-mono text-lg font-medium uppercase tracking-wide lg:text-xl"> 22 | {title} 23 | </p> 24 | </div> 25 | <p class={`mt-2 line-clamp-3 text-base text-slate-400 `}> 26 | {description} 27 | </p> 28 | </div> 29 | <footer> 30 | <div class="mt-6 inline-flex items-center space-x-1"> 31 | <p class={`text-xs font-medium text-slate-200 `}> 32 | {author.name} 33 | </p> 34 | <span aria-hidden="true">·</span> 35 | <div class={`flex text-xs text-slate-300`}> 36 | <time datetime="2020-03-16">{pubDate}</time> 37 | </div> 38 | </div> 39 | </footer> 40 | </div> 41 | </article> 42 | </a> 43 | 44 | <style> 45 | .article-overlay, 46 | .article-overlay img { 47 | } 48 | .article-overlay.article-gradient-overlay { 49 | background: linear-gradient(0deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 100%); 50 | } 51 | .article-overlay.article-gradient-overlay-bottom { 52 | background: linear-gradient(0deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 1) 5%, rgba(0, 0, 0, 0) 50%); 53 | } 54 | .article-overlay:before { 55 | content: ""; 56 | filter: url(#article-noise-filter); 57 | opacity: 0.5; 58 | z-index: 10; 59 | @apply absolute bottom-0 left-0 right-0 top-0; 60 | } 61 | </style> 62 | -------------------------------------------------------------------------------- /src/components/blog/Newsletter.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { class: className } = Astro.props; 3 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 4 | 5 | const lang = getLangFromUrl(Astro.url); 6 | const t = useTranslations(lang); 7 | const translatePath = useTranslatedPath(lang); 8 | --- 9 | 10 | <div class:list={["relative mt-8 h-20 max-w-[30rem] leading-6 lg:max-w-[35rem]", className]}> 11 | <form class="relative z-20 h-full bg-slate-200" action="https://mail.majestico.co/index.php/lists/ow388frsaw01e/subscribe" method="post"> 12 | <input 13 | type="email" 14 | name="EMAIL" 15 | id="EMAIL" 16 | autocomplete="email" 17 | placeholder={t("footer.yourmail")} 18 | class="pointer-events-auto relative m-0 h-full w-full cursor-text border-0 bg-transparent px-5 py-2 text-xl font-normal not-italic outline-none placeholder:text-gray-400 focus:ring-transparent" 19 | /> 20 | <div style="position: absolute; left: -5000px;" aria-hidden="true"> 21 | <input 22 | type="text" 23 | name="18f4a629c368e76d3c2d72326a752aa810467edd" 24 | tabindex="-1" 25 | autocomplete="18f4a629c368e76d3c2d72326a752aa810467edd" 26 | value="" 27 | /> 28 | </div> 29 | <button 30 | type="submit" 31 | aria-label="Send newsletter form button" 32 | class="pointer-events-auto absolute right-5 top-1/2 m-0 inline-block h-8 w-8 -translate-y-1/2 scale-100 transform cursor-pointer bg-transparent p-0 text-center font-normal normal-case not-italic"> 33 | <svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 34 | <path 35 | fill-rule="evenodd" 36 | clip-rule="evenodd" 37 | d="M1.9999 11.9998C1.9999 12.552 2.44762 12.9997 2.9999 12.9997H18.9757C18.8901 13.148 18.7838 13.2876 18.657 13.4144L12.2931 19.7784C11.9025 20.1689 11.9025 20.8021 12.2931 21.1926C12.6836 21.5831 13.3168 21.5831 13.7073 21.1926L22.1926 12.7073C22.5831 12.3168 22.5831 11.6836 22.1926 11.2931L22.1924 11.293L13.7071 2.80767C13.3166 2.41715 12.6834 2.41715 12.2929 2.80767C11.9024 3.1982 11.9024 3.83136 12.2929 4.22189L18.657 10.586C18.7836 10.7126 18.8896 10.8518 18.9752 10.9998H2.9999C2.44762 10.9997 1.9999 11.4475 1.9999 11.9998Z" 38 | fill="black"> 39 | </path> 40 | </svg> 41 | </button> 42 | </form> 43 | <div class="mx-4 mb-0 mt-2 text-black"></div> 44 | </div> 45 | 46 | <style> 47 | .squircle-bg-light { 48 | background: rgb(226 232 240 / 1); 49 | --squircle-smooth: 1; 50 | --squircle-radius: 10px; 51 | mask-image: paint(squircle); 52 | } 53 | </style> 54 | -------------------------------------------------------------------------------- /src/components/blog/Tag.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { type CollectionEntry } from "astro:content"; 3 | 4 | import EntriesOne from "@/components/blog/Entry.astro"; 5 | import Title from "@components/global/Title.astro"; 6 | 7 | interface Props { 8 | tag: string; 9 | posts: CollectionEntry<"posts">; 10 | } 11 | 12 | const { tag, posts } = Astro.props as Props; 13 | --- 14 | 15 | <section> 16 | <div class="mx-auto px-8 py-24 md:px-12 lg:pt-32 2xl:max-w-7xl"> 17 | <div class="py-10"> 18 | <Title title={tag} subtitle="tag" class="pb-10 pt-20 uppercase" /> 19 | </div> 20 | <ul class="mt-12 grid grid-cols-1 gap-x-3 gap-y-24 sm:grid-cols-2 lg:mt-24 lg:grid-cols-3 lg:gap-x-6"> 21 | { 22 | posts.map((post) => ( 23 | <EntriesOne 24 | url={"/blog/" + post.slug} 25 | title={post.data.title} 26 | description={post.data.description} 27 | alt={post.data.title} 28 | pubDate={post.data.pubDate.toString().slice(0, 10)} 29 | author={post.data.author} 30 | image={post.data.image.source} 31 | /> 32 | )) 33 | } 34 | </ul> 35 | </div> 36 | </section> 37 | -------------------------------------------------------------------------------- /src/components/blog/Tags.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import { Icon } from "astro-icon/components"; 4 | import Title from "@components/global/Title.astro"; 5 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 6 | 7 | const lang = getLangFromUrl(Astro.url); 8 | const t = useTranslations(lang); 9 | const translatePath = useTranslatedPath(lang); 10 | 11 | const allPosts = (await getCollection("posts")) 12 | .filter((post) => { 13 | const [postLang, ...slug] = post.slug.split("/"); 14 | return postLang === lang; 15 | }) 16 | .map((post) => { 17 | const [postLang, slug] = post.slug.split("/"); 18 | 19 | return { 20 | ...post, 21 | slug: slug, 22 | }; 23 | }) 24 | .sort((a, b) => Number(b.data.pubDate) - Number(a.data.pubDate)); 25 | 26 | const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())]; 27 | --- 28 | 29 | <section> 30 | <div class="mx-auto px-8 py-24 md:px-12 lg:pt-32 2xl:max-w-7xl"> 31 | <div> 32 | <Title class="pb-10 pt-20 uppercase" title="Tags" subtitle="lorem ipsum" /> 33 | </div> 34 | <div> 35 | <ol class="mt-12 flex flex-col divide-y divide-zinc-800 py-8"> 36 | { 37 | tags.map((tag) => ( 38 | <li class="w-full py-8 font-mono text-xl font-semibold text-zinc-800 hover:text-black md:text-2xl "> 39 | <a href={translatePath(`/tags/${tag}`)} class="flex w-full items-center justify-between"> 40 | {tag}{" "} 41 | <span class="ml-auto"> 42 | <Icon name="ic:baseline-arrow-right" width="30" /> 43 | </span> 44 | </a> 45 | </li> 46 | )) 47 | } 48 | </ol> 49 | </div> 50 | </div> 51 | </section> 52 | -------------------------------------------------------------------------------- /src/components/forms/Contact.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "@components/global/Title.astro"; 3 | 4 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 5 | 6 | const lang = getLangFromUrl(Astro.url); 7 | const t = useTranslations(lang); 8 | const translatePath = useTranslatedPath(lang); 9 | --- 10 | 11 | <section class="section isolate z-20"> 12 | <div class="z-20 col-span-full col-start-1 bg-black px-6 py-24 sm:py-32 sm:pb-72 lg:col-span-2 lg:col-start-2 lg:px-8"> 13 | <Title class="pb-10 pt-20 uppercase" title={t("contact.title")} subtitle={t("contact.subtitle")} /> 14 | <form data-static-form-name="contact" class="relative mx-auto max-w-2xl bg-black pt-12 lg:pt-20"> 15 | <div class="grid grid-cols-1 gap-x-8 gap-y-6 sm:grid-cols-2"> 16 | <div class="form-field sm:col-span-2"> 17 | <label for="name" class="form-label">{t("contact.name")}*</label> 18 | <input type="text" name="name" id="name" autocomplete="given-name" required /> 19 | </div> 20 | <div class="form-field sm:col-span-2"> 21 | <label for="email" class="form-label">{t("contact.email")}*</label> 22 | <input type="email" name="email" id="email" autocomplete="email" required /> 23 | </div> 24 | <div class="form-field sm:col-span-2"> 25 | <label for="company" class="form-label">{t("contact.company")}</label> 26 | <input type="text" name="company" id="company" autocomplete="organization" /> 27 | </div> 28 | <div class="form-field sm:col-span-2"> 29 | <label for="message" class="form-label">{t("contact.message")}*</label> 30 | <textarea name="message" id="message" rows="4" required></textarea> 31 | </div> 32 | <div class="flex gap-x-4 sm:col-span-2"> 33 | <label class="text-sm leading-6 text-gray-400" id="switch-1-label"> 34 | {t("contact.agree")} 35 | <a href="/privacy" class="font-semibold text-white">privacy policy</a>. 36 | </label> 37 | </div> 38 | </div> 39 | <div class="flex pt-10"> 40 | <div class="bg-zinc-100"> 41 | <button 42 | type="submit" 43 | class="flex h-10 w-full max-w-52 flex-1 items-center justify-center px-4 py-2 text-xl text-black transition-all hover:text-slate-800 sm:w-auto md:font-bold lg:h-10"> 44 | {t("contact.send")} 45 | </button> 46 | </div> 47 | </div> 48 | </form> 49 | </div> 50 | </section> 51 | 52 | <style> 53 | .form-field { 54 | @apply block; 55 | } 56 | 57 | .form-label { 58 | @apply flex text-sm font-semibold leading-6 text-gray-100; 59 | } 60 | 61 | input, 62 | textarea { 63 | @apply pointer-events-auto relative m-0 w-full cursor-text border-0 bg-white px-5 py-2 text-xl font-normal not-italic text-black outline-none placeholder:text-gray-900 focus:ring-slate-500; 64 | } 65 | </style> 66 | -------------------------------------------------------------------------------- /src/components/global/BackgroundLines.astro: -------------------------------------------------------------------------------- 1 | <div class="background-lines"></div> 2 | 3 | <style> 4 | div { 5 | mix-blend-mode: exclusion; 6 | flex: none; 7 | left: 0; 8 | overflow: hidden; 9 | pointer-events: none; 10 | position: fixed; 11 | top: 0; 12 | width: 100%; 13 | height: 100%; 14 | width: 100%; 15 | transform-origin: left bottom; 16 | background-image: linear-gradient(90deg, var(--grid-rows-color) 1px, transparent 1px); 17 | background-size: 25.01%; 18 | margin-left: -1px; 19 | opacity: 0.15; 20 | transform: none; 21 | z-index: 10; 22 | } 23 | </style> 24 | -------------------------------------------------------------------------------- /src/components/global/BlobAnimation.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const blobAColor = "#EDB74D"; 3 | const blobBColor = "#EB6666"; 4 | const blobCColor = "#6FB18A"; 5 | --- 6 | 7 | <div class="blob-cont"> 8 | <div class="blob-color-a blob"></div> 9 | <div class="blob-color-c blob"></div> 10 | <div class="blob-color-b blob"></div> 11 | </div> 12 | 13 | <style define:vars={{ blobAColor, blobBColor, blobCColor }}> 14 | /* Blobs */ 15 | 16 | .blob-cont { 17 | display: flex; 18 | flex-direction: column; 19 | justify-content: center; 20 | align-items: center; 21 | z-index: -2; 22 | height: 100%; 23 | width: 100%; 24 | position: absolute; 25 | } 26 | 27 | .blob { 28 | border-radius: 100px; 29 | filter: blur(80px); 30 | opacity: 50%; 31 | } 32 | 33 | .blob-color-a { 34 | background-color: var(--blobAColor); 35 | position: absolute; 36 | top: 200px; 37 | left: 100px; 38 | height: 200px; 39 | width: 200px; 40 | 41 | animation: blob-color-a 8s infinite ease; 42 | } 43 | 44 | .blob-color-b { 45 | background-color: var(--blobBColor); 46 | position: absolute; 47 | top: 80px; 48 | right: -20px; 49 | height: 200px; 50 | width: 250px; 51 | 52 | animation: blob-color-b 8s infinite ease; 53 | } 54 | 55 | .blob-color-c { 56 | background-color: var(--blobCColor); 57 | position: absolute; 58 | right: 0; 59 | top: 300px; 60 | height: 250px; 61 | width: 200px; 62 | 63 | animation: blob-color-c 8s infinite linear; 64 | } 65 | 66 | @keyframes blob-color-a { 67 | 0% { 68 | top: 200px; 69 | left: 10%; 70 | transform: scale(1); 71 | } 72 | 30% { 73 | top: 300px; 74 | left: 15%; 75 | transform: scale(1.2); 76 | } 77 | 60% { 78 | top: 100px; 79 | left: 20%; 80 | transform: scale(1.3); 81 | } 82 | 100% { 83 | top: 200px; 84 | left: 10%; 85 | transform: scale(1); 86 | } 87 | } 88 | 89 | @keyframes blob-color-b { 90 | 0% { 91 | top: 80px; 92 | right: 40%; 93 | transform: scale(1.2); 94 | } 95 | 30% { 96 | top: 300px; 97 | right: 50%; 98 | transform: scale(1); 99 | } 100 | 60% { 101 | top: 200px; 102 | right: 60%; 103 | transform: scale(1); 104 | } 105 | 100% { 106 | top: 80px; 107 | right: 40%; 108 | transform: scale(1.2); 109 | } 110 | } 111 | 112 | @keyframes blob-color-c { 113 | 0% { 114 | top: 250px; 115 | right: 75%; 116 | transform: scale(1); 117 | } 118 | 30% { 119 | top: 150px; 120 | right: 70%; 121 | transform: scale(1.4); 122 | } 123 | 60% { 124 | top: 250px; 125 | right: 80%; 126 | transform: scale(1); 127 | } 128 | 100% { 129 | top: 250px; 130 | right: 75%; 131 | transform: scale(1); 132 | } 133 | } 134 | </style> 135 | -------------------------------------------------------------------------------- /src/components/global/Footer.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Newsletter from "@components/blog/Newsletter.astro"; 3 | import { languages } from "@i18n/ui"; 4 | import { getLangFromUrl, getUrlWithoutLang, useTranslatedPath, useTranslations } from "@i18n/utils"; 5 | import { Icon } from "astro-icon/components"; 6 | 7 | const currentLang = getLangFromUrl(Astro.url); 8 | const path = getUrlWithoutLang(Astro.url); 9 | const t = useTranslations(currentLang); 10 | const translatePath = useTranslatedPath(currentLang); 11 | --- 12 | 13 | <footer class="relative flex min-h-screen w-full flex-col gap-y-10 font-mono uppercase"> 14 | <div class="relative flex-grow"></div> 15 | <div class="section gap-y-20"> 16 | <div class="col-span-full col-start-1 sm:col-span-1 sm:col-start-2"> 17 | <div class="flex flex-col gap-2 px-4"> 18 | <a href={translatePath("/")}>Home</a> 19 | <a href={translatePath("/blog/")}>Blog</a> 20 | <a href={translatePath("/tags/")}>Tags</a> 21 | <a href={translatePath("/contact/")}>{t("contacts")}</a> 22 | <a href={translatePath("/terms/")}>Terms</a> 23 | <a href={translatePath("/privacy/")}>Privacy Policy</a> 24 | <a href={translatePath("/rss.xml")}>RSS</a> 25 | </div> 26 | </div> 27 | <div class="col-span-full col-start-1 flex flex-col gap-2 sm:col-span-1 sm:col-start-3"> 28 | <a class="px-4" href="https://twitter.com/" target="_blank">Twitter / X</a> 29 | <a class="px-4" href="https://www.instagram.com/" target="_blank">Instagram</a> 30 | <a class="px-4" href="https://www.linkedin.com/company/" target="_blank">Linkedin</a> 31 | <a class="px-4" href="https://tiktok.com/" target="_blank">TikTok</a> 32 | </div> 33 | <div class="col-span-full col-start-1 flex items-end py-3 sm:col-span-1 sm:col-start-3 sm:row-start-2 lg:text-xl"> 34 | <a href="mailto:hello@yourcompany.com" class="block cursor-pointer px-4">hello@yourcompany.com</a> 35 | </div> 36 | <div 37 | class="relative col-span-2 col-start-1 mt-4 flex w-full items-end justify-between gap-y-4 py-3 text-sm sm:col-span-1 sm:col-start-1 lg:text-xl"> 38 | <div class="px-4">©2025 Your Company</div> 39 | </div> 40 | <div class="col-span-1 col-start-3 flex items-end gap-2 px-4 py-3 sm:col-span-1 sm:col-start-2 lg:text-xl"> 41 | { 42 | Object.entries(languages).map(([lang, label]) => ( 43 | <a 44 | class:list={currentLang == lang ? "bg-white text-black" : ""} 45 | href={translatePath(path, `${currentLang === "it" ? "en" : "it"}`)}> 46 | {lang} 47 | </a> 48 | )) 49 | } 50 | </div> 51 | <button id="go-top-button" aria-label="Go to Top" class="col-span-1 col-start-4 flex items-end justify-end py-2 duration-300 sm:col-start-4"> 52 | <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 24 24"> 53 | <g stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"> 54 | <path d="m15 12l-3-3m0 0l-3 3m3-3v6"></path> 55 | </g> 56 | </svg> 57 | </button> 58 | </div> 59 | </footer> 60 | 61 | <script> 62 | import { gsap } from "gsap"; 63 | import { ScrollToPlugin } from "gsap/ScrollToPlugin"; 64 | 65 | gsap.registerPlugin(ScrollToPlugin); 66 | 67 | function init() { 68 | var goTopButton = document.getElementById("go-top-button"); 69 | 70 | if (!goTopButton) return; 71 | 72 | goTopButton.addEventListener("click", function () { 73 | gsap.to(window, { duration: 0.5, scrollTo: 0 }); 74 | }); 75 | } 76 | 77 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 78 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 79 | </script> 80 | -------------------------------------------------------------------------------- /src/components/global/GrainyGradient.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const randomId = Math.random().toString(36).substring(2, 15); 3 | const filterUrlId = `url(#${randomId})`; 4 | 5 | const { colorBg = "#6cf901", colorA = "#EDB74D", colorB = "#EB6666", colorC = "#6FB18A", class: className, ...rest } = Astro.props; 6 | --- 7 | 8 | <div class:list={[className, "grainy-gradient"]} {...rest}> 9 | <svg class="hidden"> 10 | <filter id={randomId}> 11 | <feTurbulence type="fractalNoise" baseFrequency="0.6" stitchTiles="stitch"></feTurbulence> 12 | <feColorMatrix in="colorNoise" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"></feColorMatrix> 13 | <feComposite operator="in" in2="SourceGraphic" result="monoNoise"></feComposite> 14 | <feBlend in="SourceGraphic" in2="monoNoise" mode="screen"></feBlend> 15 | </filter> 16 | </svg> 17 | <div class="blob-cont"> 18 | <div class="color-a blob"></div> 19 | <div class="color-c blob"></div> 20 | <div class="color-b blob"></div> 21 | </div> 22 | <slot /> 23 | </div> 24 | 25 | <style define:vars={{ filterUrlId, colorBg, colorA, colorB, colorC }}> 26 | .grainy-gradient { 27 | position: relative; 28 | border-radius: inherit; 29 | overflow: hidden; 30 | } 31 | .grainy-gradient::before, 32 | .grainy-gradient::after { 33 | position: absolute; 34 | left: 0; 35 | top: 0; 36 | content: ""; 37 | width: 100%; 38 | height: 100%; 39 | z-index: -1; 40 | opacity: 40%; 41 | border-radius: inherit; 42 | } 43 | .grainy-gradient::before { 44 | background: var(--colorBg); 45 | filter: var(--filterUrlId); 46 | } 47 | 48 | /* Blobs */ 49 | 50 | .blob-cont { 51 | display: flex; 52 | flex-direction: column; 53 | justify-content: center; 54 | align-items: center; 55 | z-index: -2; 56 | height: 100%; 57 | width: 100%; 58 | position: absolute; 59 | } 60 | 61 | .blob { 62 | border-radius: 100px; 63 | filter: blur(80px); 64 | opacity: 50%; 65 | } 66 | 67 | .color-a { 68 | background-color: var(--colorA); 69 | position: absolute; 70 | top: 200px; 71 | left: 100px; 72 | height: 200px; 73 | width: 200px; 74 | 75 | animation: color-a 8s infinite ease; 76 | } 77 | 78 | .color-b { 79 | background-color: var(--colorB); 80 | position: absolute; 81 | top: 80px; 82 | right: -20px; 83 | height: 200px; 84 | width: 250px; 85 | 86 | animation: color-b 8s infinite ease; 87 | } 88 | 89 | .color-c { 90 | background-color: var(--colorC); 91 | position: absolute; 92 | right: 0; 93 | top: 300px; 94 | height: 250px; 95 | width: 200px; 96 | 97 | animation: color-c 8s infinite linear; 98 | } 99 | 100 | @keyframes color-a { 101 | 0% { 102 | top: 200px; 103 | left: 100px; 104 | transform: scale(1); 105 | } 106 | 30% { 107 | top: 300px; 108 | left: 150px; 109 | transform: scale(1.2); 110 | } 111 | 60% { 112 | top: 100px; 113 | left: 200px; 114 | transform: scale(1.3); 115 | } 116 | 100% { 117 | top: 200px; 118 | left: 100px; 119 | transform: scale(1); 120 | } 121 | } 122 | 123 | @keyframes color-b { 124 | 0% { 125 | top: 80px; 126 | right: -20px; 127 | transform: scale(1.2); 128 | } 129 | 30% { 130 | top: 300px; 131 | right: -20px; 132 | transform: scale(1); 133 | } 134 | 60% { 135 | top: 200px; 136 | right: 100px; 137 | transform: scale(1); 138 | } 139 | 100% { 140 | top: 80px; 141 | right: -20px; 142 | transform: scale(1.2); 143 | } 144 | } 145 | 146 | @keyframes color-c { 147 | 0% { 148 | top: 250px; 149 | right: 0px; 150 | transform: scale(1); 151 | } 152 | 30% { 153 | top: 150px; 154 | right: 150px; 155 | transform: scale(1.4); 156 | } 157 | 60% { 158 | top: 250px; 159 | right: 100px; 160 | transform: scale(1); 161 | } 162 | 100% { 163 | top: 250px; 164 | right: 0px; 165 | transform: scale(1); 166 | } 167 | } 168 | </style> 169 | -------------------------------------------------------------------------------- /src/components/global/LinkBlend.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { class: className, isButton, ...rest } = Astro.props; 3 | --- 4 | 5 | { 6 | isButton ? ( 7 | <button class:list={[className]} {...rest}> 8 | <slot /> 9 | </button> 10 | ) : ( 11 | <a class:list={[className]} {...rest}> 12 | <slot /> 13 | </a> 14 | ) 15 | } 16 | 17 | <style> 18 | a, 19 | button { 20 | cursor: pointer; 21 | display: inline-block; 22 | color: white; 23 | text-decoration: none; 24 | position: relative; 25 | transition: all 0.2s cubic-bezier(0.445, 0.05, 0.55, 0.95); 26 | z-index: 1; 27 | } 28 | a::after, 29 | button::after { 30 | content: ""; 31 | background: #ffffff; 32 | /* mix-blend-mode: exclusion; */ 33 | width: calc(100% + 9px); 34 | height: 1px; 35 | position: absolute; 36 | bottom: -2px; 37 | left: -4px; 38 | transition: all 0.2s cubic-bezier(0.445, 0.05, 0.55, 0.95); 39 | } 40 | a:hover, 41 | button:hover { 42 | color: black; 43 | } 44 | a:hover::after, 45 | button:hover::after { 46 | height: calc(100% + 4px); 47 | z-index: -1; 48 | } 49 | </style> 50 | -------------------------------------------------------------------------------- /src/components/global/Navigation.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from "astro:assets"; 3 | import { Icon } from "astro-icon/components"; 4 | import { getLangFromUrl, getUrlWithoutLang, useTranslations, useTranslatedPath } from "@i18n/utils"; 5 | 6 | const lang = getLangFromUrl(Astro.url); 7 | const path = getUrlWithoutLang(Astro.url); 8 | const t = useTranslations(lang); 9 | const translatePath = useTranslatedPath(lang); 10 | 11 | let pages = [ 12 | { 13 | id: "home", 14 | initialTitle: "H", 15 | title: "Home", 16 | class: "home", 17 | url: "/", 18 | }, 19 | { 20 | id: "services", 21 | initialTitle: "S", 22 | title: t("services"), 23 | class: "services", 24 | url: "/services", 25 | }, 26 | { 27 | id: "blog", 28 | initialTitle: "B", 29 | title: "Blog", 30 | class: "blog", 31 | url: "/blog/", 32 | }, 33 | { 34 | id: "contact", 35 | initialTitle: "C", 36 | title: t("contacts"), 37 | class: "contact", 38 | url: "/contact/", 39 | }, 40 | ]; 41 | 42 | const { pathname } = Astro.url; 43 | --- 44 | 45 | <header class="header section"> 46 | { 47 | pages.map((page) => ( 48 | <div class={`menu-item ${page.class}`} data-item-id={page.id}> 49 | <a 50 | href={translatePath(page.url)} 51 | data-title={page.title} 52 | class="flex h-full items-center justify-center py-3 font-mono text-xs font-bold uppercase sm:text-base md:text-xl"> 53 | {page.title} 54 | </a> 55 | </div> 56 | )) 57 | } 58 | </header> 59 | 60 | <style> 61 | header.header { 62 | mix-blend-mode: exclusion; 63 | height: auto; 64 | position: fixed; 65 | z-index: 99; 66 | gap: 0; 67 | } 68 | </style> 69 | 70 | <script> 71 | import { gsap } from "gsap"; 72 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 73 | import { ScrambleTextPlugin } from "gsap/ScrambleTextPlugin"; 74 | 75 | gsap.registerPlugin(ScrollTrigger, ScrambleTextPlugin); 76 | 77 | function init() { 78 | const homeScrambleTl = gsap 79 | .timeline({ 80 | defaults: { 81 | duration: 0.8, 82 | ease: "power4.inOut", 83 | }, 84 | }) 85 | .from("header .menu-item.home a", { 86 | scrambleText: { 87 | text: "H", 88 | speed: 0.4, 89 | }, 90 | }); 91 | 92 | const servicesScrambleTl = gsap 93 | .timeline({ 94 | defaults: { 95 | duration: 0.8, 96 | ease: "power4.inOut", 97 | }, 98 | }) 99 | .from("header .menu-item.services a", { 100 | scrambleText: { 101 | text: "S", 102 | speed: 0.4, 103 | }, 104 | }); 105 | 106 | const blogScrambleTl = gsap 107 | .timeline({ 108 | defaults: { 109 | duration: 0.8, 110 | ease: "power4.inOut", 111 | }, 112 | }) 113 | .from("header .menu-item.blog a", { 114 | scrambleText: { 115 | text: "B", 116 | speed: 0.4, 117 | }, 118 | }); 119 | 120 | const contactScrambleTl = gsap 121 | .timeline({ 122 | defaults: { 123 | duration: 0.8, 124 | ease: "power4.inOut", 125 | }, 126 | }) 127 | .from("header .menu-item.contact a", { 128 | scrambleText: { 129 | text: "C", 130 | speed: 0.4, 131 | }, 132 | }); 133 | 134 | const headerScrambleTl = gsap 135 | .timeline() 136 | .add(homeScrambleTl) 137 | .add(servicesScrambleTl, "<=+0.2") 138 | .add(blogScrambleTl, "<=+0.2") 139 | .add(contactScrambleTl, "<=+0.2"); 140 | 141 | ScrollTrigger.create({ 142 | start: "top top", 143 | end: "max", 144 | onUpdate: (self) => { 145 | self.direction === -1 ? headerScrambleTl.timeScale(1).play() : headerScrambleTl.timeScale(1.5).reverse(); 146 | }, 147 | }); 148 | 149 | // On mouse hover if the text is scramble unscramble it 150 | } 151 | 152 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 153 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 154 | </script> 155 | -------------------------------------------------------------------------------- /src/components/global/Title.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | title: string; 4 | subtitle: string; 5 | class?: string; 6 | center?: boolean; 7 | } 8 | 9 | const { title, subtitle, class: className, center = true } = Astro.props; 10 | --- 11 | 12 | <div class:list={[`flex ${center ? "justify-center" : ""}`, className]}> 13 | <div class={`inline-flex flex-col justify-center gap-4 ${center ? "items-center text-center" : ""}`}> 14 | <div class={`inline-flex items-center ${center ? " justify-center" : ""}`}> 15 | <span class="font-mono text-xs font-semibold uppercase tracking-wide" set:html={subtitle} /> 16 | </div> 17 | <h1 class="font-display text-5xl font-extrabold sm:text-6xl md:text-7xl xl:text-9xl">{title}</h1> 18 | </div> 19 | </div> 20 | 21 | <style> 22 | h1 { 23 | word-break: break-word; 24 | } 25 | </style> 26 | -------------------------------------------------------------------------------- /src/components/infopages/Error404.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "../global/Title.astro"; 3 | --- 4 | 5 | <section class="isolate px-6 py-24 sm:py-32 lg:px-8"> 6 | <div class="grid min-h-full place-items-center px-6 py-24 sm:py-32 lg:px-8"> 7 | <div class="text-center"> 8 | <div class:list={["flex justify-center pb-10 pt-20 uppercase"]}> 9 | <div class="inline-flex flex-col gap-4"> 10 | <div class="inline-flex items-center justify-center gap-4"> 11 | <span class="font-mono text-xs font-semibold uppercase tracking-wide">404</span> 12 | </div> 13 | <h1 class="font-display text-3xl font-extrabold sm:text-4xl md:text-5xl lg:text-6xl">Page not found</h1> 14 | </div> 15 | </div> 16 | 17 | <p class="mt-6 text-base leading-7 text-gray-600">Sorry, we couldn't find the page you're looking for.</p> 18 | <div class="mt-10 flex items-center justify-center gap-x-6"> 19 | <div class="rounded-lg bg-zinc-900"> 20 | <a 21 | class="flex h-10 w-full max-w-52 flex-1 items-center justify-center px-4 py-2 text-xl text-slate-200 transition-all hover:text-white sm:w-auto md:font-bold lg:h-10" 22 | href="/"> 23 | Homepage 24 | </a> 25 | </div> 26 | <a href="/contact/" class="text-sm font-semibold text-gray-900">Contact support <span aria-hidden="true">→</span></a> 27 | </div> 28 | </div> 29 | </div> 30 | </section> 31 | 32 | <style> 33 | .gradient-element { 34 | @apply relative left-1/2 -z-10 aspect-[1155/678] w-[36.125rem] max-w-none -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-40rem)] sm:w-[72.1875rem]; 35 | clip-path: polygon( 36 | 74.1% 44.1%, 37 | 100% 61.6%, 38 | 97.5% 26.9%, 39 | 85.5% 0.1%, 40 | 80.7% 2%, 41 | 72.5% 32.5%, 42 | 60.2% 62.4%, 43 | 52.4% 68.1%, 44 | 47.5% 58.3%, 45 | 45.2% 34.5%, 46 | 27.5% 76.7%, 47 | 0.1% 64.9%, 48 | 17.9% 100%, 49 | 27.6% 76.8%, 50 | 76.1% 97.7%, 51 | 74.1% 44.1% 52 | ); 53 | } 54 | </style> 55 | -------------------------------------------------------------------------------- /src/components/infopages/Faq.astro: -------------------------------------------------------------------------------- 1 | <section> 2 | <div class="px-8 py-24 mx-auto md:px-12 2xl:max-w-7xl lg:pt-32"> 3 | <div class="p-8 lg:p-20 bg-blue-700 rounded-4xl lg:rounded-6xl"> 4 | <div class="inline-flex items-center gap-4"> 5 | <div class="w-20 h-1 bg-white hidden md:block"></div> 6 | <span class="text-xs font-semibold tracking-wide uppercase text-white" 7 | >faq.</span 8 | > 9 | </div> 10 | 11 | <p 12 | class="mt-8 text-4xl font-display font-semibold md:text-6xl lg:text-9xl text-white"> 13 | frequent questions 14 | 15 | <span class="md:block md:text-lime-500"> & answers</span> 16 | </p> 17 | </div> 18 | <div class="mt-6 divide-y space-y-3 max-w-3xl mx-auto"> 19 | <details> 20 | <summary 21 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 22 | How do I get started with your service? 23 | </summary> 24 | <p class="text-slate-500 text-base pt-5"> 25 | To get started with our service, simply visit our website and sign up 26 | for an account. From there, you can explore our products and services 27 | and choose the ones that best meet your needs. 28 | </p> 29 | </details> 30 | 31 | <details> 32 | <summary 33 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 34 | How can I request a quote for my project? 35 | </summary> 36 | <p class="text-slate-500 text-base pt-5"> 37 | Requesting a quote is easy! Just visit our Contact page and provide us 38 | with the details of your project. Our team will review your 39 | requirements and get back to you with a tailored quote. 40 | </p> 41 | </details> 42 | 43 | <details> 44 | <summary 45 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 46 | What makes your agency stand out? 47 | </summary> 48 | <p class="text-slate-500 text-base pt-5"> 49 | We combine creative expertise with technical excellence. Our team's 50 | collaborative approach ensures that every project is infused with 51 | innovation, design excellence, and cutting-edge technology. 52 | </p> 53 | </details> 54 | 55 | <details> 56 | <summary 57 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 58 | Can you handle both small startups and larger businesses? 59 | </summary> 60 | <p class="text-slate-500 text-base pt-5"> 61 | Absolutely! We have experience working with a diverse range of 62 | clients, from startups to established enterprises. Our solutions are 63 | tailored to fit the unique needs of each project. 64 | </p> 65 | </details> 66 | 67 | <details> 68 | <summary 69 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 70 | How long does it take to complete a typical project? 71 | </summary> 72 | <p class="text-slate-500 text-base pt-5"> 73 | The project timeline varies depending on the complexity and scope. We 74 | work closely with our clients to set realistic timelines and 75 | milestones to ensure timely delivery without compromising quality. 76 | </p> 77 | </details> 78 | 79 | <details> 80 | <summary 81 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 82 | Do you offer ongoing maintenance and support? 83 | </summary> 84 | <p class="text-slate-500 text-base pt-5"> 85 | Yes, we provide ongoing support and maintenance services to ensure 86 | your digital assets remain up-to-date and secure. Our team is 87 | available to address any concerns and implement updates. 88 | </p> 89 | </details> 90 | 91 | <details> 92 | <summary 93 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 94 | How do you ensure my website is responsive on all devices? 95 | </summary> 96 | <p class="text-slate-500 text-base pt-5"> 97 | We follow industry best practices for responsive design, conducting 98 | thorough testing on various devices and screen sizes. This ensures 99 | your website delivers a seamless user experience across platforms. 100 | </p> 101 | </details> 102 | 103 | <details> 104 | <summary 105 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 106 | Can you help with SEO and digital marketing strategies? 107 | </summary> 108 | <p class="text-slate-500 text-base pt-5"> 109 | Absolutely! We specialize in SEO optimization and digital marketing 110 | services to enhance your online visibility. Our experts will work with 111 | you to create effective strategies that drive traffic and engagement. 112 | </p> 113 | </details> 114 | 115 | <details> 116 | <summary 117 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 118 | What technologies do you specialize in? 119 | </summary> 120 | <p class="text-slate-500 text-base pt-5"> 121 | We specialize in a wide range of technologies including HTML5, CSS3, 122 | JavaScript, React, Node.js, and more. Our team stays updated with the 123 | latest trends to ensure your projects are built using the best tools 124 | available. 125 | </p> 126 | </details> 127 | 128 | <details> 129 | <summary 130 | class="text-black text-lg lg:text-3xl font-medium tracking-wide focus:text-slate-500 i py-3 text-left w-full font-display"> 131 | How do I get started with your agency? 132 | </summary> 133 | <p class="text-slate-500 text-base pt-5"> 134 | Getting started is simple! Reach out to us through our Contact page or 135 | give us a call. We'll discuss your project requirements, goals, and 136 | next steps to kick-start the exciting journey of collaboration. 137 | </p> 138 | </details> 139 | </div> 140 | </div> 141 | </section> 142 | -------------------------------------------------------------------------------- /src/components/infopages/Privacy.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "@components/global/Title.astro"; 3 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 4 | 5 | const lang = getLangFromUrl(Astro.url); 6 | const t = useTranslations(lang); 7 | const translatePath = useTranslatedPath(lang); 8 | --- 9 | 10 | <section> 11 | <div class="mx-auto max-w-7xl px-8 py-12 md:px-12 lg:px-32 lg:py-32"> 12 | <div> 13 | <Title title="Privacy" subtitle="Last updated 01. Jan 2024" class="pb-10 pt-20 uppercase" /> 14 | </div> 15 | <div class="prose-styles mt-12 lg:mt-24"> 16 | <h2 id="shine-the-light-disclosure">{t("privacy.wip")}</h2> 17 | <p>{t("privacy.wip.content")}</p> 18 | </div> 19 | </div> 20 | </section> 21 | -------------------------------------------------------------------------------- /src/components/infopages/Terms.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "@components/global/Title.astro"; 3 | --- 4 | 5 | <section> 6 | <div class="mx-auto max-w-7xl px-8 py-12 md:px-12 lg:px-32 lg:py-32"> 7 | <div> 8 | <Title title="Terms" subtitle="Last updated January 1, 2024" class="pb-10 pt-20 uppercase" /> 9 | </div> 10 | <div class="prose-styles mt-12 lg:mt-24"> 11 | <h2 id="description-all-the-terms-that-you-agree-to-when-you-sign-up-for-a-Majestico-product-"> 12 | All the terms that you agree to when you sign up for a Majestico product. 13 | </h2> 14 | 15 | <p> 16 | From everyone at Majestico, thank you for using our products! We build them to help you do your best work. There are millions of 17 | people using Majestico products every day. Because we don't know every one of our customers personally, we have to put in place some 18 | Terms of Service to help keep the ship afloat. 19 | </p> 20 | <p> 21 | When we say “Company”, “we”, “our”, or “us” in this document, we are referring to Majestico, a leading agency in [industry/field]. We 22 | specialize in [brief description of services and products]. 23 | </p> 24 | <p> 25 | When we say “Services”, we mean our websites, including [www.majestico.com], and any product created and maintained by Majestico, such 26 | as [list specific services or products]. 27 | </p> 28 | <p> 29 | When we say “You” or “your”, we are referring to the people or organizations that own an account with one or more of our Services. Our 30 | products are designed to empower [your target audience] in [specific ways]. 31 | </p> 32 | 33 | <p>WIP.</p> 34 | 35 | <p>If you have any questions about these Terms, please contact us at [support@majestico.it].</p> 36 | </div> 37 | </div> 38 | </section> 39 | -------------------------------------------------------------------------------- /src/components/infopages/ThankYou.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "../global/Title.astro"; 3 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 4 | 5 | const lang = getLangFromUrl(Astro.url); 6 | const t = useTranslations(lang); 7 | const translatePath = useTranslatedPath(lang); 8 | --- 9 | 10 | <section class="isolate px-6 py-24 sm:py-32 lg:px-8"> 11 | <div class="grid min-h-full place-items-center px-6 py-24 sm:py-32 lg:px-8"> 12 | <div class="text-center"> 13 | <div class:list={["flex justify-center pb-10 pt-20 uppercase"]}> 14 | <div class="inline-flex flex-col gap-4"> 15 | <div class="inline-flex items-center justify-center gap-4"> 16 | <span class="font-mono text-xs font-semibold uppercase tracking-wide">{t("thanks.subtitle")}</span> 17 | </div> 18 | <h1 class="font-display text-3xl font-extrabold sm:text-4xl md:text-5xl lg:text-6xl">{t("thanks.title")}</h1> 19 | </div> 20 | </div> 21 | 22 | <p class="mt-6 text-base leading-7 text-gray-600">{t("thanks.content")}</p> 23 | <div class="mt-10 flex items-center justify-center gap-x-6"> 24 | <div class="rounded-lg bg-zinc-900"> 25 | <a 26 | class="flex h-10 w-full max-w-52 flex-1 items-center justify-center px-4 py-2 text-xl text-slate-200 transition-all hover:text-white sm:w-auto md:font-bold lg:h-10" 27 | href={translatePath("/")}> 28 | {t("homepage")} 29 | </a> 30 | </div> 31 | </div> 32 | </div> 33 | </div> 34 | </section> 35 | 36 | <style> 37 | .gradient-element { 38 | @apply relative left-1/2 -z-10 aspect-[1155/678] w-[36.125rem] max-w-none -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-40rem)] sm:w-[72.1875rem]; 39 | clip-path: polygon( 40 | 74.1% 44.1%, 41 | 100% 61.6%, 42 | 97.5% 26.9%, 43 | 85.5% 0.1%, 44 | 80.7% 2%, 45 | 72.5% 32.5%, 46 | 60.2% 62.4%, 47 | 52.4% 68.1%, 48 | 47.5% 58.3%, 49 | 45.2% 34.5%, 50 | 27.5% 76.7%, 51 | 0.1% 64.9%, 52 | 17.9% 100%, 53 | 27.6% 76.8%, 54 | 76.1% 97.7%, 55 | 74.1% 44.1% 56 | ); 57 | } 58 | </style> 59 | -------------------------------------------------------------------------------- /src/components/landing/AllInOne.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { class: className } = Astro.props; 3 | --- 4 | 5 | <!-- The best thing? It's all in one --> 6 | <section id="all-in-one" class:list={["all-in-one-section relative text-white", className]}> 7 | <div class="wrapper col-span-12 min-h-screen w-full"> 8 | <div class="line-animation-wrapper h-full min-h-[110rem]"> 9 | <svg id="all-in-one-svg" class="absolute" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 1200"> 10 | <!-- https://yqnn.github.io/svg-path-editor/ --> 11 | <path 12 | class="all-in-one-line-path" 13 | d="M -5 0 Q 334 135 189 164 T 227 362 A 1 1 0 0 0 302 268 A 1 1 0 0 0 215 410 C 274 443 324 481 322 552 C 310 703 365 610 983 719" 14 | fill="none" 15 | stroke="white" 16 | stroke-width="5px"> 17 | </path> 18 | </svg> 19 | </div> 20 | 21 | <div class="min-h-[150rem]"> 22 | <div> 23 | <h2>Best thing?</h2> 24 | </div> 25 | <div> 26 | <h3>It's all in 1 package</h3> 27 | </div> 28 | </div> 29 | </div> 30 | </section> 31 | 32 | <script> 33 | import { gsap } from "gsap"; 34 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 35 | import { DrawSVGPlugin } from "gsap/DrawSVGPlugin"; 36 | 37 | gsap.registerPlugin(ScrollTrigger, DrawSVGPlugin); 38 | 39 | function init() { 40 | const linePath = document.querySelector(".all-in-one-line-path"); 41 | 42 | gsap.set(linePath, { drawSVG: 0 }); 43 | 44 | gsap.timeline({ 45 | scrollTrigger: { 46 | trigger: ".line-animation-wrapper", 47 | scrub: 1, 48 | start: "top center", 49 | end: "bottom center", 50 | markers: false, 51 | }, 52 | defaults: { ease: "none" }, 53 | }).to( 54 | linePath, 55 | { 56 | drawSVG: "100%", 57 | // ease: "power4.in", 58 | }, 59 | 0, 60 | ); 61 | } 62 | 63 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 64 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 65 | </script> 66 | -------------------------------------------------------------------------------- /src/components/landing/Cta.astro: -------------------------------------------------------------------------------- 1 | <section> 2 | <div class="px-8 py-12 mx-auto md:px-12 2xl:max-w-7xl lg:py-24"> 3 | <div class="p-8 lg:p-20 bg-blue-700 rounded-4xl lg:rounded-6xl text-center"> 4 | <div class="inline-flex items-center gap-4"> 5 | <div class="w-20 h-1 bg-white hidden md:block"></div> 6 | <span class="text-xs font-semibold tracking-wide uppercase text-white"> 7 | Alex Carter, Founder of NexusWeb Solutions.</span 8 | > 9 | </div> 10 | <p 11 | class="mt-8 text-4xl font-display font-semibold md:text-6xl lg:text-8xl text-white"> 12 | Elevate your digital vision with us. Contact now to start creating the 13 | extraordinary! 14 | </p> 15 | <div class="flex mt-10 justify-center"> 16 | <a 17 | class="flex items-center justify-center w-full h-10 px-8 rounded-full py-2 text-sm font-semibold text-black transition-all lg:h-16 bg-lime-500 hover:bg-lime-400 lg:w-auto" 18 | href="/forms/contact"> 19 | Contact us    →</a 20 | > 21 | </div> 22 | </div> 23 | </div> 24 | </section> 25 | -------------------------------------------------------------------------------- /src/components/landing/Description.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getLangFromUrl, useTranslatedPath, useTranslations } from "@i18n/utils"; 3 | 4 | const { class: className } = Astro.props; 5 | 6 | const lang = getLangFromUrl(Astro.url); 7 | const t = useTranslations(lang); 8 | const translatePath = useTranslatedPath(lang); 9 | --- 10 | 11 | <!-- The best thing? It's all in one --> 12 | <div id="majestico-motto" class:list={["section motto-section gap-y-20 py-20 text-xl md:text-2xl", className]}> 13 | <div id="scramble-text-subtitle" class="scramble-text col-span-full col-start-1 break-words px-4 font-mono sm:col-span-1 sm:col-start-2"> 14 | {t("hero.subtitle")} 15 | </div> 16 | <div 17 | id="scramble-text-tagline" 18 | class="scramble-text col-span-full col-start-1 row-start-2 break-words px-4 font-mono sm:col-span-1 sm:col-start-3"> 19 | {t("tagline")} 20 | </div> 21 | </div> 22 | 23 | <style></style> 24 | 25 | <script> 26 | import { gsap } from "gsap"; 27 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 28 | import { ScrambleTextPlugin } from "gsap/ScrambleTextPlugin"; 29 | import { SplitText } from "gsap/SplitText"; 30 | 31 | gsap.registerPlugin(ScrollTrigger, ScrambleTextPlugin, SplitText); 32 | 33 | function init() { 34 | var scrambleSubtitleText = new SplitText("#scramble-text-subtitle", { type: "words" }); 35 | var scrambleTaglineText = new SplitText("#scramble-text-tagline", { type: "words" }); 36 | 37 | var subtitleTl = gsap.timeline({ 38 | defaults: { duration: 1, ease: "none" }, 39 | scrollTrigger: { 40 | trigger: "#scramble-text-subtitle", 41 | start: "start bottom", 42 | end: "center 80%", 43 | toggleActions: "play none resume reset", 44 | markers: false, 45 | }, 46 | }); 47 | 48 | scrambleSubtitleText.words.forEach((word) => { 49 | subtitleTl.from( 50 | word, 51 | { 52 | duration: 0.5, 53 | scrambleText: { 54 | text: "{original}", 55 | chars: "lowerCase", 56 | revealDelay: 0.5, 57 | speed: 0.5, 58 | tweenLength: false, 59 | }, 60 | }, 61 | 0, 62 | ); 63 | }); 64 | 65 | var taglineTl = gsap.timeline({ 66 | defaults: { duration: 1, ease: "none" }, 67 | scrollTrigger: { 68 | trigger: "#scramble-text-tagline", 69 | start: "start bottom", 70 | end: "center 80%", 71 | toggleActions: "play none resume reset", 72 | markers: false, 73 | }, 74 | }); 75 | 76 | scrambleTaglineText.words.forEach((word) => { 77 | taglineTl.from( 78 | word, 79 | { 80 | duration: 0.5, 81 | scrambleText: { 82 | text: "{original}", 83 | chars: "lowerCase", 84 | revealDelay: 0.5, 85 | speed: 0.5, 86 | tweenLength: false, 87 | }, 88 | }, 89 | 0, 90 | ); 91 | }); 92 | } 93 | 94 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 95 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 96 | </script> 97 | -------------------------------------------------------------------------------- /src/components/landing/Facts.astro: -------------------------------------------------------------------------------- 1 | <section> 2 | <div class="px-8 py-12 mx-auto md:px-12 2xl:max-w-7xl lg:py-24"> 3 | <div class="grid grid-cols-1 gap-4 xl:grid-cols-2 items-start"> 4 | <div class="p-8 lg:p-20 bg-blue-700 rounded-4xl lg:rounded-5xl"> 5 | <div class="inline-flex items-center gap-4"> 6 | <div class="w-20 h-1 bg-white hidden md:block"></div> 7 | <span class="text-xs font-semibold tracking-wide uppercase text-white" 8 | >Unmatched Excellence.</span 9 | > 10 | </div> 11 | <p 12 | class="text-4xl mt-8 font-display lg:text-8xl font-semibold text-white"> 13 | Unmatched <span class="lg:block lg:text-lime-500">Excellence</span> 14 | </p> 15 | <p class="text-white text-base mt-8"> 16 | We are a collective of highly skilled digital product specialists, 17 | each bringing a unique set of talents and expertise to the table. 18 | <br /> 19 | <br /> 20 | Our team is composed of individuals hailing from diverse backgrounds and 21 | cultures, uniting our strengths to create exceptional digital experiences. 22 | <br /> 23 | <br /> 24 | With members from all corners of the globe, we thrive on the synergy of 25 | our international collaboration, drawing inspiration and innovation from 26 | a multitude of perspectives. 27 | </p> 28 | </div> 29 | <ol 30 | role="list" 31 | class="grid grid-cols-1 xl:grid-cols-1 md:grid-cols-3 lg:text-center items-center rounded-4xl lg:rounded-5xl gap-4 h-full p-8 lg:p-20 lg:grid-cols-2 list-none bg-lime-500"> 32 | <li> 33 | <h3 34 | class="text-lg lg:text-3xl tracking-wide font-semibold text-black md:mt-0 font-display"> 35 | Over 30 Years of Agency Experience 36 | </h3> 37 | <p class="mt-2 text-base text-black"> 38 | With over three years in operation, we've achieved a remarkable 39 | track record, successfully launching projects worldwide. 40 | </p> 41 | </li> 42 | <li> 43 | <h3 44 | class="text-lg lg:text-3xl tracking-wide font-semibold text-black md:mt-0 font-display"> 45 | 1000+ Content Clients 46 | </h3> 47 | <p class="mt-2 text-base text-black"> 48 | We gauge our triumph by clients who repeatedly choose to 49 | collaborate. More than half of our clients entrust us beyond a 50 | solitary project. 51 | </p> 52 | </li> 53 | <li> 54 | <h3 55 | class="text-lg lg:text-3xl tracking-wide font-semibold text-black md:mt-0 font-display"> 56 | Facilitated Fundraising of Over $20B 57 | </h3> 58 | <p class="mt-2 text-base text-black"> 59 | Our efforts have contributed to securing over $20 billion in funding 60 | for pleny enterprises. 61 | </p> 62 | </li> 63 | </ol> 64 | </div> 65 | </div> 66 | </section> 67 | -------------------------------------------------------------------------------- /src/components/landing/Faq.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 3 | 4 | const { class: className } = Astro.props; 5 | 6 | const lang = getLangFromUrl(Astro.url); 7 | const t = useTranslations(lang); 8 | const translatePath = useTranslatedPath(lang); 9 | 10 | const faqs = [ 11 | { 12 | question: t("faqs.question1"), 13 | answer: t("faqs.answer1"), 14 | }, 15 | { 16 | question: t("faqs.question2"), 17 | answer: t("faqs.answer2"), 18 | }, 19 | { 20 | question: t("faqs.question3"), 21 | answer: t("faqs.answer3"), 22 | }, 23 | { 24 | question: t("faqs.question4"), 25 | answer: t("faqs.answer4"), 26 | }, 27 | { 28 | question: t("faqs.question5"), 29 | answer: t("faqs.answer5"), 30 | }, 31 | { 32 | question: t("faqs.question6"), 33 | answer: t("faqs.answer6"), 34 | }, 35 | { 36 | question: t("faqs.question7"), 37 | answer: t("faqs.answer7"), 38 | }, 39 | { 40 | question: t("faqs.question8"), 41 | answer: t("faqs.answer8"), 42 | }, 43 | { 44 | question: t("faqs.question9"), 45 | answer: t("faqs.answer9"), 46 | }, 47 | { 48 | question: t("faqs.question10"), 49 | answer: t("faqs.answer10"), 50 | }, 51 | ]; 52 | --- 53 | 54 | <section id="faq" class:list={["section faq-section relative bg-zinc-900 text-white", className]}> 55 | <div class="wrapper col-span-12 flex min-h-screen w-full flex-col py-32 md:flex-row"> 56 | <div class="title-container top-0 pt-10 md:sticky md:h-[30rem]"> 57 | <h2 class="font-display text-5xl md:text-6xl lg:text-8xl">FAQ</h2> 58 | </div> 59 | <div class="faq-list flex grow flex-col items-stretch px-0 pt-10 md:px-20 lg:px-40"> 60 | <!-- https://codepen.io/frogmcw/pen/deqRwa --> 61 | <div class="hidden"> 62 | <svg xmlns="http://www.w3.org/2000/svg"> 63 | <symbol viewBox="0 0 24 24" id="expand-more"> 64 | <path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"></path><path d="M0 0h24v24H0z" fill="none"></path> 65 | </symbol> 66 | <symbol viewBox="0 0 24 24" id="close"> 67 | <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path><path 68 | d="M0 0h24v24H0z" 69 | fill="none"> 70 | </path> 71 | </symbol> 72 | </svg> 73 | </div> 74 | 75 | { 76 | faqs.map(({ question, answer }) => ( 77 | <details class="relative min-h-1 grow rounded-md border-zinc-800 bg-zinc-800 text-lg transition-all"> 78 | <summary class="gap-4 px-11 py-11"> 79 | {question} 80 | <svg class="control-icon control-icon-expand" width="24" height="24" role="presentation"> 81 | <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#expand-more" /> 82 | </svg> 83 | <svg class="control-icon control-icon-close" width="24" height="24" role="presentation"> 84 | <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#close" /> 85 | </svg> 86 | </summary> 87 | <p class="px-11 pb-11 font-light text-zinc-200">{answer}</p> 88 | </details> 89 | )) 90 | } 91 | </div> 92 | </div> 93 | <div class="faq-more-questions col-span-12 flex flex-col items-center justify-center gap-8 pb-32"> 94 | <div class="flex gap-10"> 95 | <span class="other-question-title opacity-0">{t("faq.otherquestions")}</span> 96 | <svg 97 | fill="transparent" 98 | stroke="#fff" 99 | stroke-width="40px" 100 | width="40" 101 | height="40" 102 | xmlns="http://www.w3.org/2000/svg" 103 | xmlns:xlink="http://www.w3.org/1999/xlink" 104 | viewBox="0 0 800 800" 105 | xml:space="preserve"> 106 | <g> 107 | <path 108 | id="faq-more-question-path" 109 | d="M779.3,519.1c-16.3,0-31,0-47.3-1.6c62-154.9,101.1-415.9-112.5-458.3C569,47.8,520,54.3,485.8,82 110 | c-58.7-35.9-132.1-53.8-195.7-45.7c-42.4,4.9-71.8,32.6-89.7,68.5c-39.1-13-81.5-17.9-110.9-4.9C-21.4,147.3-29.5,294,66.7,362.5 111 | c9.8,6.5,17.9-6.5,11.4-14.7C-1.8,259.8,40.6,82,180.8,139.1c1.6,1.6,4.9,1.6,6.5,3.3c-11.4,44-8.2,94.6,11.4,130.5 112 | c42.4,78.3,145.1,26.1,125.6-53.8c-9.8-37.5-45.7-73.4-89.7-96.2c13-26.1,34.2-45.7,70.1-47.3c48.9-1.6,99.5,9.8,141.9,34.2 113 | c4.9,3.3,8.2,4.9,13,8.2c-4.9,11.4-9.8,24.5-13,39.1c-9.8,48.9,1.6,146.8,55.4,171.2c66.9,29.4,102.7-75,84.8-123.9 114 | c-13-37.5-39.1-70.1-70.1-96.2c13-9.8,31-14.7,53.8-13c225,16.3,177.8,274,119.1,422.4c-16.3-1.6-34.2-4.9-52.2-6.5 115 | c-3.3,0-6.5,0-8.2,1.6c-9.8-8.2-27.7-4.9-27.7,11.4c-3.3,71.8,21.2,156.6,40.8,226.7c4.9,16.3,31,21.2,39.1,4.9 116 | c35.9-70.1,76.6-135.4,115.8-203.8C805.4,538.7,794,519.1,779.3,519.1z M285.2,220.6c21.2,52.2-47.3,65.2-58.7,26.1 117 | c-4.9-16.3-8.2-34.2-8.2-52.2c0-11.4,1.6-22.8,3.3-34.2C247.7,175,272.2,196.2,285.2,220.6z M492.3,135.8 118 | c27.7,19.6,50.6,45.7,57.1,76.6c1.6,11.4,1.6,83.2-24.5,81.5c-34.2-1.6-44-83.2-44-106C480.9,170.1,484.2,152.2,492.3,135.8z 119 | M642.4,540.3c32.6,8.2,66.9,14.7,101.1,19.6c-24.5,40.8-48.9,81.5-71.8,123.9C663.6,634.9,657,584.3,642.4,540.3z"> 120 | </path> 121 | </g> 122 | </svg> 123 | </div> 124 | <div class="faq-contact-us-button rounded-lg bg-zinc-200 opacity-0 sm:ml-44"> 125 | <a 126 | class="flex h-10 w-full max-w-52 flex-1 items-center justify-center px-4 py-2 text-xl text-zinc-900 transition-all hover:text-black sm:w-auto md:font-bold lg:h-10" 127 | href={translatePath("/contact/")}> 128 | {t("contactus")} 129 | </a> 130 | </div> 131 | </div> 132 | </section> 133 | 134 | <style> 135 | details + details { 136 | margin-top: 20px; 137 | } 138 | 139 | details[open] { 140 | min-height: 50px; 141 | box-shadow: 2px 2px 20px rgba(0, 0, 0, 0.2); 142 | } 143 | 144 | summary { 145 | display: flex; 146 | justify-content: space-between; 147 | align-items: center; 148 | font-weight: 500; 149 | cursor: pointer; 150 | } 151 | 152 | summary:focus { 153 | outline: none; 154 | } 155 | 156 | summary:focus::after { 157 | content: ""; 158 | height: 100%; 159 | width: 100%; 160 | display: block; 161 | position: absolute; 162 | top: 0; 163 | left: 0; 164 | } 165 | 166 | summary::-webkit-details-marker { 167 | display: none; 168 | } 169 | 170 | .control-icon { 171 | fill: white; 172 | transition: 0.3s ease; 173 | pointer-events: none; 174 | } 175 | 176 | .control-icon-close { 177 | display: none; 178 | } 179 | 180 | details[open] .control-icon-close { 181 | display: initial; 182 | transition: 0.3s ease; 183 | } 184 | 185 | details[open] .control-icon-expand { 186 | display: none; 187 | } 188 | </style> 189 | 190 | <style is:global> 191 | .faq-section { 192 | --faq-clip: 0; 193 | --faq-radius: 0; 194 | clip-path: inset(var(--faq-clip) round var(--faq-radius)); 195 | margin-top: -1px; /* Fix weird clip path behavior */ 196 | } 197 | </style> 198 | 199 | <script> 200 | import { gsap } from "gsap"; 201 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 202 | import { DrawSVGPlugin } from "gsap/DrawSVGPlugin"; 203 | 204 | gsap.registerPlugin(ScrollTrigger, DrawSVGPlugin); 205 | 206 | function init() { 207 | gsap.to("#faq", { 208 | scrollTrigger: { 209 | trigger: "#faq", 210 | start: "bottom center", 211 | end: "bottom top", 212 | scrub: 1, 213 | markers: false, 214 | }, 215 | "--faq-clip": "2vw", 216 | "--faq-radius": "5rem", 217 | }); 218 | 219 | const linePath = document.querySelector("#faq-more-question-path"); 220 | 221 | gsap.set(linePath, { drawSVG: 0 }); 222 | 223 | let otherQTl = gsap 224 | .timeline({ 225 | scrollTrigger: { 226 | trigger: ".faq-more-questions", 227 | start: "center 90%", 228 | end: "center top", 229 | markers: false, 230 | toggleActions: "play none none reverse", 231 | }, 232 | }) 233 | .to(".faq-more-questions .other-question-title", { 234 | opacity: 1, 235 | duration: 0.5, 236 | }) 237 | .to( 238 | linePath, 239 | { 240 | drawSVG: "100%", 241 | duration: 2, 242 | }, 243 | "<", 244 | ) 245 | .to( 246 | ".faq-contact-us-button", 247 | { 248 | opacity: 1, 249 | }, 250 | ">-=1.5", 251 | ); 252 | } 253 | 254 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 255 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 256 | </script> 257 | -------------------------------------------------------------------------------- /src/components/landing/Hero.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import LinkBlend from "@components/global/LinkBlend.astro"; 3 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 4 | 5 | const lang = getLangFromUrl(Astro.url); 6 | const t = useTranslations(lang); 7 | const translatePath = useTranslatedPath(lang); 8 | --- 9 | 10 | <section id="hero" class="section dark-section hero min-h-screen"> 11 | <div class="mx-auto flex h-full flex-col"> 12 | <div class="relative flex flex-1 flex-col justify-center gap-8 rounded-lg py-10 pb-28 md:gap-20 md:pb-10 lg:rounded-2xl"> 13 | <div class="flex justify-center"> 14 | <h1 class="sr-only">Majestico</h1> 15 | <!--?xml version="1.0" encoding="utf-8"?--> 16 | <!-- Generator: Adobe Illustrator 27.7.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> 17 | <svg 18 | version="1.1" 19 | id="majestico-logo-svg" 20 | xmlns="http://www.w3.org/2000/svg" 21 | xmlns:xlink="http://www.w3.org/1999/xlink" 22 | viewBox="0 0 1333.5 196" 23 | enable-background="new 0 0 1333.5 196" 24 | class="title-svg w-[min(90rem,90vw)]" 25 | xml:space="preserve"> 26 | <g id="M"> 27 | <defs> 28 | <path 29 | id="M_x5F_Mask" 30 | d="M36.2,53.4v131.1H13.8V10h29.9l57.1,141.5h1.2L159.1,10H189v174.4h-22.4V53.4H165L112,184.4H90.8 31 | L37.7,53.4H36.2z"> 32 | </path> 33 | </defs> 34 | <clipPath id="M_x5F_Mask_00000096770518741426740400000009189978287050670243_"> 35 | <use xlink:href="#M_x5F_Mask" overflow="visible"></use> 36 | </clipPath> 37 | 38 | <polyline 39 | id="M_x5F_Path" 40 | clip-path="url(#M_x5F_Mask_00000096770518741426740400000009189978287050670243_)" 41 | fill="none" 42 | stroke="#FFFFFF" 43 | class="logo-path-mask" 44 | stroke-width="32" 45 | stroke-miterlimit="10" 46 | points=" 47 | 25,187.2 27,10 101.4,184.4 174.3,10 182,186 "> 48 | </polyline> 49 | </g> 50 | <g id="A"> 51 | <defs> 52 | <path 53 | id="A_x5F_Mask" 54 | d="M322,132.1h-74l-19.9,52.3h-23.9l67-174.4h27.4l67,174.4h-23.9L322,132.1z M285.9,36.7h-2l-28.4,75.5 55 | h58.8L285.9,36.7z"> 56 | </path> 57 | </defs> 58 | <clipPath id="A_x5F_Mask_00000037678601105432892380000012529143596060145033_"> 59 | <use xlink:href="#A_x5F_Mask" overflow="visible"></use> 60 | </clipPath> 61 | 62 | <polyline 63 | id="A_x5F_Path" 64 | clip-path="url(#A_x5F_Mask_00000037678601105432892380000012529143596060145033_)" 65 | fill="none" 66 | stroke="#FFFFFF" 67 | class="logo-path-mask" 68 | stroke-width="32" 69 | stroke-miterlimit="10" 70 | points=" 71 | 217.9,191 284.9,10 329,119.7 358.5,187.5 329,119.7 245.7,119 "> 72 | </polyline> 73 | </g> 74 | <g id="J"> 75 | <defs> 76 | <path 77 | id="J_x5F_Mask" 78 | d="M378.3,123.4h22.4c0,14.5,2.4,25.2,7.2,32.1c4.8,7,12.7,10.5,23.7,10.5s18.9-3.4,23.8-10.3 79 | c4.9-6.9,7.4-17.6,7.4-32.3V10h22.4v113.4c0,42.5-17.9,63.8-53.6,63.8C396.1,187.2,378.3,165.9,378.3,123.4z"> 80 | </path> 81 | </defs> 82 | <clipPath id="J_x5F_Mask_00000119094009115737901400000004815366034736149687_"> 83 | <use xlink:href="#J_x5F_Mask" overflow="visible"></use> 84 | </clipPath> 85 | 86 | <path 87 | id="J_x5F_Path" 88 | clip-path="url(#J_x5F_Mask_00000119094009115737901400000004815366034736149687_)" 89 | fill="none" 90 | class="logo-path-mask" 91 | stroke="#FFFFFF" 92 | stroke-width="32" 93 | stroke-miterlimit="10" 94 | d=" 95 | M387.8,120c0,0,4.7,64.5,44,58.3C471,172,474,141,474,141V10"> 96 | </path> 97 | </g> 98 | <g id="E"> 99 | <defs> 100 | <path id="E_x5F_Mask" d="M626.8,164.5v19.9H509.2V10h117.6v19.9h-95.2v57.3h83v19.9h-83v57.3H626.8z"></path> 101 | </defs> 102 | <clipPath id="E_x5F_Mask_00000098198300135129560250000010768648456595972771_"> 103 | <use xlink:href="#E_x5F_Mask" overflow="visible"></use> 104 | </clipPath> 105 | 106 | <polyline 107 | id="E_x5F_Path" 108 | clip-path="url(#E_x5F_Mask_00000098198300135129560250000010768648456595972771_)" 109 | fill="none" 110 | class="logo-path-mask" 111 | stroke="#FFFFFF" 112 | stroke-width="32" 113 | stroke-miterlimit="10" 114 | points=" 115 | 628,19.3 521.3,19.3 521.3,97.2 623,94.3 521.3,97.2 521.3,178.7 629,178.7 "> 116 | </polyline> 117 | </g> 118 | <g id="S"> 119 | <defs> 120 | <path 121 | id="S_x5F_Mask" 122 | d="M724,86.7c18.9,3.7,32.8,9.6,41.7,17.7c8.9,8.1,13.3,19.1,13.3,32.9c0,16.3-6.1,28.7-18.2,37.1 123 | c-12.1,8.5-27.7,12.7-46.8,12.7c-8.8,0-17.1-0.9-24.9-2.6c-7.8-1.7-15-4.5-21.7-8.3c-6.6-3.8-11.9-9.1-15.8-15.8 124 | c-3.9-6.7-5.9-14.6-5.9-23.5v-1.7h23.7v1.2c0,19.8,14.9,29.7,44.6,29.7c12.3,0,22.3-2.3,29.9-7c7.6-4.6,11.5-11.7,11.5-21.2 125 | c0-8-2.7-14.1-8-18.4c-5.3-4.3-15-8-29.2-11l-19.9-4.2c-31.9-6.8-47.8-22.8-47.8-48.1c0-15,5.8-26.8,17.4-35.6 126 | c11.6-8.8,26.2-13.2,43.9-13.2c20.3,0,35.8,4.4,46.6,13.3c10.8,8.9,16.2,21,16.2,36.3v1.2h-23.9v-1c0-9-3.2-16-9.7-21.1 127 | c-6.5-5.1-15.9-7.6-28.4-7.6c-11.5,0-20.7,2.4-27.8,7.2c-7.1,4.8-10.6,11.4-10.6,19.7c0,8.6,2.7,15,8,18.9c5.3,4,13.1,7,23.4,9 128 | L724,86.7z"> 129 | </path> 130 | </defs> 131 | <clipPath id="S_x5F_Mask_00000048492160201443585990000017206624758945857183_"> 132 | <use xlink:href="#S_x5F_Mask" overflow="visible"></use> 133 | </clipPath> 134 | 135 | <path 136 | id="S_x5F_Path" 137 | clip-path="url(#S_x5F_Mask_00000048492160201443585990000017206624758945857183_)" 138 | fill="none" 139 | class="logo-path-mask" 140 | stroke="#FFFFFF" 141 | stroke-width="32" 142 | stroke-miterlimit="10" 143 | d=" 144 | M765.2,63c0,0-3.5-48.4-52.8-43.7s-50.3,19.2-50.3,19.2S652,69,681,88c0,0,58,12,68,19s21,10,16,34s-4,27-25,32s-68.5-1-73.2-7 145 | s-6.4-33-6.4-33"> 146 | </path> 147 | </g> 148 | <g id="T"> 149 | <defs> 150 | <path id="T_x5F_Mask" d="M788,29.9V10h134.1v19.9h-55.8v154.5h-22.4V29.9H788z"></path> 151 | </defs> 152 | <clipPath id="T_x5F_Mask_00000116951300969580086640000012441786884314804641_"> 153 | <use xlink:href="#T_x5F_Mask" overflow="visible"></use> 154 | </clipPath> 155 | 156 | <polyline 157 | id="T_x5F_Path" 158 | clip-path="url(#T_x5F_Mask_00000116951300969580086640000012441786884314804641_)" 159 | fill="none" 160 | class="logo-path-mask" 161 | stroke="#FFFFFF" 162 | stroke-width="32" 163 | stroke-miterlimit="10" 164 | points=" 165 | 855,185 855,18.9 787,19.3 924,18.9 "> 166 | </polyline> 167 | </g> 168 | <g id="I"> 169 | <defs> 170 | <path id="I_x5F_Mask" d="M960.9,10v174.4h-22.4V10H960.9z"></path> 171 | </defs> 172 | <clipPath id="I_x5F_Mask_00000171721944521342947480000007976815643322046373_"> 173 | <use xlink:href="#I_x5F_Mask" overflow="visible"></use> 174 | </clipPath> 175 | 176 | <line 177 | id="I_x5F_Path" 178 | clip-path="url(#I_x5F_Mask_00000171721944521342947480000007976815643322046373_)" 179 | fill="none" 180 | stroke="#FFFFFF" 181 | class="logo-path-mask" 182 | stroke-width="32" 183 | stroke-miterlimit="10" 184 | x1="949.7" 185 | y1="9" 186 | x2="949.7" 187 | y2="187"> 188 | </line> 189 | </g> 190 | <g id="C"> 191 | <defs> 192 | <path 193 | id="C_x5F_Mask" 194 | d="M1137.9,74.3h-23.4c-1.7-14.5-7.2-25.7-16.7-33.8c-9.5-8.1-21.6-12.1-36.4-12.1 195 | c-16.9,0-30.4,6.2-40.4,18.7c-10,12.5-15,29.6-15,51.3c0,21.1,5,37.6,15,49.6c10,12,23.4,17.9,40.4,17.9 196 | c13.3,0,24.7-3.7,34.3-11.1c9.6-7.4,15.4-17.6,17.6-30.5h23.9c-2.7,19.8-10.8,35.2-24.4,46.2c-13.6,11-30.7,16.6-51.3,16.6 197 | c-24.4,0-43.7-8.1-57.8-24.2c-14.1-16.1-21.2-37.6-21.2-64.5c0-27.6,7.1-49.7,21.3-66.3c14.2-16.6,33.4-24.9,57.7-24.9 198 | c22.3,0,40,5.9,53.3,17.6C1128,36.5,1135.7,53,1137.9,74.3z"> 199 | </path> 200 | </defs> 201 | <clipPath id="C_x5F_Mask_00000062872912612276495140000008034629371513245619_"> 202 | <use xlink:href="#C_x5F_Mask" overflow="visible"></use> 203 | </clipPath> 204 | 205 | <path 206 | id="C_x5F_Path" 207 | clip-path="url(#C_x5F_Mask_00000062872912612276495140000008034629371513245619_)" 208 | fill="none" 209 | class="logo-path-mask" 210 | stroke="#FFFFFF" 211 | stroke-width="32" 212 | stroke-miterlimit="10" 213 | d=" 214 | M1129.3,79c0,0-14.9-61.1-63.1-60.1C1018,20,1001,53,1001,53s-12.3,34.3-7.6,59.2s25.5,53.8,25.5,53.8s44.8,15.4,51,12.7 215 | s54.7-23.3,59.5-60.7"> 216 | </path> 217 | </g> 218 | <g id="O"> 219 | <defs> 220 | <path 221 | id="O_x5F_Mask" 222 | d="M1178.6,32.2c14.5-16.6,34.3-24.9,59.4-24.9c25.1,0,44.8,8.3,59.1,24.9c14.3,16.6,21.4,38.7,21.4,66.3 223 | c0,26.9-7.1,48.4-21.4,64.5c-14.3,16.1-34,24.2-59.1,24.2c-25.1,0-44.9-8.1-59.4-24.2c-14.5-16.1-21.8-37.6-21.8-64.5 224 | C1156.8,70.9,1164.1,48.8,1178.6,32.2z M1238,166c17.6,0,31.5-6,41.6-17.9c10.1-12,15.2-28.5,15.2-49.6c0-21.8-5.1-38.9-15.2-51.3 225 | c-10.1-12.5-24-18.7-41.6-18.7c-17.6,0-31.6,6.2-42,18.7c-10.4,12.5-15.6,29.6-15.6,51.3c0,21.1,5.1,37.6,15.5,49.6 226 | C1206.2,160,1220.3,166,1238,166z"> 227 | </path> 228 | </defs> 229 | <clipPath id="O_x5F_Mask_00000011747446453532554160000011773768268075636659_"> 230 | <use xlink:href="#O_x5F_Mask" overflow="visible"></use> 231 | </clipPath> 232 | 233 | <path 234 | id="O_x5F_Path" 235 | clip-path="url(#O_x5F_Mask_00000011747446453532554160000011773768268075636659_)" 236 | fill="none" 237 | class="logo-path-mask" 238 | stroke="#FFFFFF" 239 | stroke-width="32" 240 | stroke-miterlimit="10" 241 | d=" 242 | M1180.5,46c0,0-11.5,23-11.5,36s4.5,48.6,5.7,53.8s13.1,24.2,23.7,30.2s36.1,11.7,47.3,8.9c11.2-2.9,41-11.3,49.1-22.1 243 | s13.9-49,14-55.9C1309,90,1306,61,1300,51s-26.6-33.4-34.8-31.7c-8.2,1.7-41.9-9.5-48.5-0.4C1210,28,1180.5,46,1180.5,46z"> 244 | </path> 245 | </g> 246 | </svg> 247 | </div> 248 | <div class="flex flex-col items-center gap-5 text-center lg:gap-10"> 249 | <div class="flex flex-col items-center justify-center gap-3 lg:col-start-1"> 250 | <small> 251 | ( <span class="scroll-text"> 252 | <LinkBlend class="text-xs lg:text-lg" id="go-projects-button" isButton={true}>{t("hero.scroll")}</LinkBlend> 253 | </span> ) 254 | </small> 255 | </div> 256 | </div> 257 | </div> 258 | </div> 259 | </section> 260 | 261 | <style> 262 | .hero { 263 | padding-bottom: 0; 264 | position: relative; 265 | border-radius: inherit; 266 | overflow: hidden; 267 | } 268 | .hero > div { 269 | grid-column: 1 / span 4; 270 | } 271 | 272 | #hero .title-svg { 273 | opacity: 0; 274 | } 275 | 276 | /* Hero appearance */ 277 | .hero h1, 278 | .hero h2, 279 | .hero h3 { 280 | margin: 0; 281 | } 282 | .hero h1 { 283 | margin: 0; 284 | position: relative; 285 | overflow: hidden; 286 | } 287 | .hero h1 > div { 288 | position: relative; 289 | margin: 0; 290 | } 291 | </style> 292 | 293 | <script> 294 | import { gsap } from "gsap"; 295 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 296 | import { SplitText } from "gsap/SplitText"; 297 | import { ScrollToPlugin } from "gsap/ScrollToPlugin"; 298 | import { DrawSVGPlugin } from "gsap/DrawSVGPlugin"; 299 | 300 | gsap.registerPlugin(ScrollTrigger, SplitText, ScrollToPlugin, DrawSVGPlugin); 301 | 302 | function init() { 303 | var goProjectsButton = document.getElementById("go-projects-button"); 304 | goProjectsButton?.addEventListener("click", function () { 305 | gsap.to(window, { duration: 0.5, scrollTo: "#majestico-motto" }); 306 | }); 307 | 308 | gsap.set("#hero .title-svg", { opacity: 1 }); 309 | 310 | gsap.timeline({ 311 | defaults: { 312 | duration: 1, 313 | ease: "power2.out", 314 | stagger: 0.1, 315 | }, 316 | }).from("#hero .title-svg .logo-path-mask", { drawSVG: 0 }); 317 | 318 | // https://codepen.io/zank/pen/PoLLOXB?editors=0010 319 | // Show scroll title from the middle 320 | let split = new SplitText("#hero .scroll-text", { type: "chars" }); 321 | gsap.timeline({ 322 | defaults: { 323 | duration: 0.3, 324 | }, 325 | }).from(split.chars, { 326 | opacity: 0, 327 | stagger: { 328 | from: "center", //try "center" and "edges" 329 | each: 0.05, 330 | }, 331 | }); 332 | } 333 | 334 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 335 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 336 | </script> 337 | -------------------------------------------------------------------------------- /src/components/landing/Services.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from "astro:assets"; 3 | import advertisingSvg from "@assets/images/services/advertising.svg?raw"; 4 | import designSvg from "@assets/images/services/creative-designer-doing-multitasking.svg?raw"; 5 | import mobileSvg from "@assets/images/services/mobile-app-ui-ux-design.svg?raw"; 6 | import seoSvg from "@assets/images/services/seo-link-building.svg?raw"; 7 | import websiteSvg from "@assets/images/services/website-ui-ux-development.svg?raw"; 8 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 9 | 10 | const lang = getLangFromUrl(Astro.url); 11 | const t = useTranslations(lang); 12 | const translatePath = useTranslatedPath(lang); 13 | 14 | const { class: className } = Astro.props; 15 | 16 | const points = [ 17 | { 18 | title: t("websites.title"), 19 | content: t("websites.content"), 20 | image: websiteSvg, 21 | }, 22 | { 23 | title: t("apps.title"), 24 | content: t("apps.content"), 25 | image: mobileSvg, 26 | }, 27 | { 28 | title: t("uiux.title"), 29 | content: t("uiux.content"), 30 | image: designSvg, 31 | }, 32 | { 33 | title: t("seo.title"), 34 | content: t("seo.content"), 35 | image: seoSvg, 36 | }, 37 | { 38 | title: t("advertising.title"), 39 | content: t("advertising.content"), 40 | image: advertisingSvg, 41 | }, 42 | ]; 43 | --- 44 | 45 | <section id="services" class:list={["section home-dark-section services-section relative", className]}> 46 | <div class="wrapper col-span-12 h-screen w-full"> 47 | <h2 class="sr-only">{t("services")}</h2> 48 | <div class="indicators absolute left-0 top-0 mt-[10vh] hidden h-[80vh] w-1 flex-col justify-between gap-2"> 49 | {points.map(() => <div class="indicator w-full grow bg-[#333333]" />)} 50 | </div> 51 | { 52 | points.map(({ title, content, image }) => ( 53 | <div class="point flex h-screen w-full flex-col items-center justify-center gap-x-20 gap-y-12 p-16 pr-0 md:flex-row lg:gap-x-40"> 54 | <div class="hidden w-full max-w-80 lg:block"> 55 | <Fragment set:html={image} class="" /> 56 | </div> 57 | <article class="flex flex-col gap-14"> 58 | <h3 class="faq font-display text-4xl uppercase md:text-5xl lg:text-6xl">{title}</h3> 59 | <p class="max-w-2xl text-lg" set:html={content} /> 60 | </article> 61 | </div> 62 | )) 63 | } 64 | </div> 65 | </section> 66 | 67 | <script> 68 | import { gsap } from "gsap"; 69 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 70 | import { MorphSVGPlugin } from "gsap/MorphSVGPlugin"; 71 | 72 | gsap.registerPlugin(ScrollTrigger, MorphSVGPlugin); 73 | 74 | function init() { 75 | var points = gsap.utils.toArray(".point") as gsap.DOMTarget[]; 76 | var indicators = gsap.utils.toArray(".indicator") as gsap.DOMTarget[]; 77 | 78 | var height = 100 * points.length; 79 | 80 | gsap.set(".indicators", { display: "flex" }); 81 | 82 | var tl = gsap.timeline({ 83 | duration: points.length, 84 | scrollTrigger: { 85 | trigger: "#services", 86 | start: "top center", 87 | end: "+=" + height + "%", 88 | scrub: 0.5, 89 | markers: false, 90 | id: "points", 91 | immediateRender: false, 92 | }, 93 | }); 94 | 95 | var pinner = gsap.timeline({ 96 | scrollTrigger: { 97 | trigger: ".services-section .wrapper", 98 | start: "top top", 99 | end: "+=" + height + "%", 100 | scrub: 0.5, 101 | pin: ".services-section .wrapper", 102 | pinSpacing: true, 103 | id: "pinning", 104 | markers: false, 105 | snap: { 106 | snapTo: [0, 0.25, 0.5, 0.7, 0.9], // snap to the closest label in the timeline 107 | duration: { min: 0.1, max: 0.1 }, // the snap animation should be at least 0.2 seconds, but no more than 3 seconds (determined by velocity) 108 | delay: 1, // wait 1 seconds from the last scroll event before doing the snapping 109 | inertia: false, 110 | directional: false, 111 | ease: "power1.inOut", // the ease of the snap animation ("power3" by default) 112 | }, 113 | }, 114 | }); 115 | 116 | points.forEach(function (elem, i) { 117 | gsap.set(elem, { position: "absolute", top: 0 }); 118 | 119 | // https://codepen.io/zank/pen/oNVjYMM?editors=0010 120 | tl.to(indicators[i], { backgroundColor: "white", duration: 0.25 }, i); 121 | tl.from((elem as HTMLElement).querySelector("svg"), { autoAlpha: 0 }, i); 122 | tl.from((elem as HTMLElement).querySelector("article"), { autoAlpha: 0, translateY: 100 }, i); 123 | 124 | if (i != points.length - 1) { 125 | tl.to(indicators[i], { backgroundColor: "#333333", duration: 0.25 }, i + 0.75); 126 | tl.to((elem as HTMLElement).querySelector("article"), { autoAlpha: 0, translateY: -100 }, i + 0.75); 127 | tl.to((elem as HTMLElement).querySelector("svg"), { autoAlpha: 0 }, i + 0.75); 128 | } 129 | }); 130 | } 131 | 132 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 133 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 134 | </script> 135 | -------------------------------------------------------------------------------- /src/components/landing/Tagline.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 3 | 4 | const { class: className } = Astro.props; 5 | 6 | const lang = getLangFromUrl(Astro.url); 7 | const t = useTranslations(lang); 8 | const translatePath = useTranslatedPath(lang); 9 | --- 10 | 11 | <!-- The best thing? It's all in one --> 12 | <div id="smooth-wrapper" class:list={["section tagline-section relative bg-zinc-900 text-white", className]}> 13 | <div class="tagline-arrows"> 14 | <svg 15 | stroke-linecap="round" 16 | class="h-[15rem] w-[15rem] rotate-45 md:w-[20rem] md:rotate-0 lg:w-[25rem]" 17 | stroke-linejoin="round" 18 | viewBox="0 0 550 330"> 19 | <path 20 | id="tagline-arrow-path" 21 | fill="none" 22 | stroke="#fff" 23 | stroke-width="10" 24 | d="M8.5,7.8C46.3,67,121.7,185.5,265.5,185.9c42.8,0.1,120.5-55.9-25.2-120.3 25 | C79-5.8,177.6,164.1,222.3,207.7c59.1,49.9,83.8,62.1,162.6,96.8"> 26 | </path> 27 | <path class="opacity-0" id="tagline-arrow" stroke="white" stroke-width="12" d="M465.7 172.9l47.1 6-29.8 33.3-17.3-39.3z"></path> 28 | </svg> 29 | </div> 30 | <div class="text col-span-12 flex justify-center font-light"> 31 | <p id="services-tagline" class="max-w-5xl text-center text-4xl md:text-5xl lg:text-7xl"> 32 | {t("tagline")} 33 | </p> 34 | </div> 35 | </div> 36 | 37 | <style> 38 | .text > p > :global(div) { 39 | background: linear-gradient(to right, rgb(255, 255, 255) 50%, rgb(37, 37, 37) 50%); 40 | background-size: 200% 100%; 41 | background-position-x: 100%; 42 | color: transparent; 43 | background-clip: text; 44 | -webkit-background-clip: text; 45 | line-height: 1.2; 46 | } 47 | </style> 48 | 49 | <script> 50 | import { gsap } from "gsap"; 51 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 52 | import { SplitText } from "gsap/SplitText"; 53 | import { DrawSVGPlugin } from "gsap/DrawSVGPlugin"; 54 | import { MotionPathPlugin } from "gsap/MotionPathPlugin"; 55 | 56 | gsap.registerPlugin(ScrollTrigger, SplitText, DrawSVGPlugin, MotionPathPlugin); 57 | 58 | function init() { 59 | const linePath = document.querySelector("#tagline-arrow-path"); 60 | 61 | gsap.set(linePath, { drawSVG: 0 }); 62 | 63 | let otherQTl = gsap 64 | .timeline({ 65 | scrollTrigger: { 66 | trigger: ".tagline-arrows", 67 | start: "center bottom", 68 | end: "center center", 69 | markers: false, 70 | scrub: 1, 71 | }, 72 | }) 73 | .to( 74 | linePath, 75 | { 76 | drawSVG: "100%", 77 | }, 78 | "<", 79 | ) 80 | .to( 81 | "#tagline-arrow", 82 | { 83 | opacity: 1, 84 | duration: 0.1, 85 | }, 86 | "<", 87 | ) 88 | .to( 89 | "#tagline-arrow", 90 | { 91 | motionPath: { 92 | path: "#tagline-arrow-path", 93 | align: "#tagline-arrow-path", 94 | alignOrigin: [0.5, 0.5], 95 | autoRotate: 30, 96 | }, 97 | }, 98 | 0, 99 | ); 100 | 101 | const split = new SplitText("#services-tagline", { type: "lines" }); 102 | 103 | split.lines.forEach((target) => { 104 | gsap.to(target, { 105 | backgroundPositionX: 0, 106 | ease: "none", 107 | scrollTrigger: { 108 | trigger: target, 109 | markers: false, 110 | scrub: 1, 111 | start: "top bottom-=20%", 112 | end: "bottom bottom-=10%", 113 | }, 114 | }); 115 | }); 116 | } 117 | 118 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 119 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 120 | </script> 121 | -------------------------------------------------------------------------------- /src/components/landing/Testimonials.astro: -------------------------------------------------------------------------------- 1 | <section> 2 | <div class="px-8 py-12 mx-auto md:px-12 2xl:max-w-7xl"> 3 | <div class="p-8 lg:p-20 bg-blue-700 rounded-4xl lg:rounded-6xl"> 4 | <div class="inline-flex items-center gap-4"> 5 | <div class="w-20 h-1 bg-white hidden md:block"></div> 6 | <span class="text-xs font-semibold tracking-wide uppercase text-white"> 7 | Alex Carter, Founder of NexusWeb Solutions.</span 8 | > 9 | </div> 10 | <p 11 | class="mt-8 text-4xl font-display font-semibold md:text-6xl lg:text-8xl text-white"> 12 | "This web agency exceeded my expectations. Their creative prowess and 13 | technical skills transformed our website into a masterpiece. A 14 | remarkable team that delivers results!" 15 | </p> 16 | </div> 17 | </div> 18 | </section> 19 | -------------------------------------------------------------------------------- /src/components/landing/WorkPreview.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from "astro:assets"; 3 | import { getCollection } from "astro:content"; 4 | import yourProjectImage from "@assets/images/projects/yourProject.png"; 5 | import WokCard from "@components/work/WokCard.astro"; 6 | import { getLangFromUrl, useTranslatedPath, useTranslations } from "@i18n/utils"; 7 | 8 | const lang = getLangFromUrl(Astro.url); 9 | const t = useTranslations(lang); 10 | const translatePath = useTranslatedPath(lang); 11 | 12 | const { class: className } = Astro.props; 13 | 14 | const steps = [10, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96]; 15 | 16 | const randomSteps = (minStep: number, maxStep: number) => { 17 | // Filter the steps array to include only values within the specified range 18 | const filteredSteps = steps.filter((step) => step >= minStep && step <= maxStep); 19 | 20 | // Generate a random index within the range of the filteredSteps array 21 | const randomIndex = Math.floor(Math.random() * filteredSteps.length); 22 | 23 | // Return the step at the random index 24 | return filteredSteps[randomIndex]; 25 | }; 26 | 27 | const possiblePositions = [ 28 | { 29 | column: 1, 30 | row: 1, 31 | translateY: 40, 32 | }, 33 | { 34 | column: 3, 35 | row: 2, 36 | translateY: randomSteps(60, 80), 37 | }, 38 | { 39 | column: 2, 40 | row: 3, 41 | translateY: randomSteps(40, 80), 42 | }, 43 | { 44 | column: 1, 45 | row: 4, 46 | translateY: randomSteps(20, 40), 47 | }, 48 | { 49 | column: 3, 50 | row: 5, 51 | translateY: randomSteps(20, 80), 52 | }, 53 | { 54 | column: 2, 55 | row: 6, 56 | translateY: randomSteps(60, 96), 57 | }, 58 | ]; 59 | 60 | const allProjects = (await getCollection("projects")) 61 | .filter((prj) => { 62 | const [postLang, ...slug] = prj.slug.split("/"); 63 | return postLang === lang; 64 | }) 65 | .map((prj) => { 66 | const [postLang, slug] = prj.slug.split("/"); 67 | 68 | return { 69 | ...prj, 70 | slug: slug, 71 | }; 72 | }) 73 | .sort((a, b) => Number(a.data.order) - Number(b.data.order)); 74 | --- 75 | 76 | <section id="projects" class:list={["work-preview-section py-12", className]}> 77 | <h2 class="sr-only">{t("projects")}</h2> 78 | <ol class="grid auto-rows-auto grid-cols-1 grid-rows-3 justify-start gap-10 gap-y-48 px-5 md:grid-cols-4"> 79 | { 80 | allProjects.map((project, index) => ( 81 | <WokCard 82 | image={project.data.image?.source} 83 | video={project.data.video} 84 | project={project.data.title} 85 | link={`/work/${project.slug}`} 86 | class:list={[ 87 | possiblePositions[index] 88 | ? `col-span-full col-start-1 translate-y-40 md:col-span-2 md:translate-y-${possiblePositions[index].translateY} md:col-start-${possiblePositions[index].column} md:row-start-${possiblePositions[index].row}` 89 | : "", 90 | ]} 91 | /> 92 | )) 93 | } 94 | </ol> 95 | </section> 96 | 97 | <script> 98 | import { gsap } from "gsap"; 99 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 100 | 101 | gsap.registerPlugin(ScrollTrigger); 102 | 103 | function init() { 104 | const projects = gsap.utils.toArray(".work-preview-section ol li") as gsap.DOMTarget[]; 105 | 106 | if (window.innerWidth >= 768) { 107 | projects.forEach((project) => { 108 | gsap.to(project, { 109 | scrollTrigger: { 110 | trigger: project, 111 | start: "top bottom", 112 | end: "bottom top", 113 | scrub: 1, 114 | markers: false, 115 | }, 116 | ease: "none", 117 | y: () => { 118 | let min = 10; 119 | let max = 20; 120 | return `-=${Math.floor(Math.random() * (max - min + 1)) + min}rem`; 121 | }, 122 | }); 123 | }); 124 | } else { 125 | projects.forEach((project) => { 126 | gsap.to(project, { 127 | scrollTrigger: { 128 | trigger: project, 129 | start: "top-=50% bottom", 130 | end: "top+=20% bottom", 131 | scrub: 1, 132 | markers: false, 133 | }, 134 | ease: "back.out(2)", 135 | y: "-=10rem", 136 | }); 137 | }); 138 | } 139 | } 140 | 141 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 142 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 143 | </script> 144 | -------------------------------------------------------------------------------- /src/components/services/ServicesMain.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "@/components/global/Title.astro"; 3 | --- 4 | 5 | <section class="section"> 6 | <div class="col-span-full col-start-1 sm:col-span-2 sm:col-start-2"> 7 | <Title title="Services" subtitle="Coming soon" class="pt-44" /> 8 | </div> 9 | </section> 10 | -------------------------------------------------------------------------------- /src/components/work/CaseStudy.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Title from "@components/global/Title.astro"; 3 | --- 4 | 5 | <section> 6 | <div class="mx-auto px-8 py-24 md:px-12 lg:pt-32 2xl:max-w-7xl"> 7 | <div> 8 | <div> 9 | <Title title="GreenLeaf Organic Grocers" subtitle="branding" class="pb-10 pt-20" /> 10 | 11 | <p class="max-w-2xl text-base text-slate-500"> 12 | <em> 13 | Transforming GreenLeaf Organic Grocers vision into a recognizable brand. We designed a logo that embodies their commitment to 14 | sustainability, and developed comprehensive brand guidelines to maintain a cohesive image across all touchpoints. 15 | </em> 16 | </p> 17 | </div> 18 | <div class="col-span-full mt-12 grid grid-cols-2 gap-4"> 19 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy1.png" alt="" /> 20 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy2.png" alt="" /> 21 | </div> 22 | <div class="prose-styles mx-auto mt-12"> 23 | <h2>Client: GreenLeaf Organic Grocers</h2> 24 | <h2>Industry: Organic Food Retail</h2> 25 | <h2>Challenge: Establishing a Strong Brand Identity to Reflect Sustainability</h2> 26 | 27 | <h3>Introduction:</h3> 28 | <p> 29 | GreenLeaf Organic Grocers, a prominent player in the organic food retail sector, approached us with a critical need to revamp 30 | their brand identity. They aspired to embody their commitment to sustainability and provide a cohesive brand experience to their 31 | customers. 32 | </p> 33 | 34 | <h3>Solution:</h3> 35 | <p> 36 | Our team embarked on a holistic brand transformation journey that aimed to capture GreenLeaf's values and vision. We devised a 37 | comprehensive strategy that involved logo design, visual identity development, and brand guidelines creation. 38 | </p> 39 | 40 | <h4>Logo Design:</h4> 41 | <p> 42 | Understanding GreenLeaf's dedication to eco-friendliness, we conceptualized a logo that represented growth, nature, and 43 | sustainability. The logo featured a vibrant leaf intertwined with a circular motif, symbolizing the cycle of nature. The color 44 | palette we selected harmonized earthy tones, evoking a sense of freshness and natural abundance. 45 | </p> 46 | </div> 47 | </div> 48 | 49 | <div class="prose-styles col-span-full mx-auto mt-12"> 50 | <h4>Visual Identity:</h4> 51 | <p> 52 | To ensure consistency across all touchpoints, we carefully curated a visual identity that reflected GreenLeaf's core values. We 53 | incorporated leaf-inspired patterns, organic textures, and nature-themed imagery. These elements were seamlessly integrated into the 54 | website, packaging, and marketing materials, fostering a strong brand presence. 55 | </p> 56 | 57 | <h4>Brand Guidelines:</h4> 58 | <p> 59 | To maintain a unified brand image, we meticulously developed comprehensive brand guidelines. These guidelines encompassed logo usage, 60 | typography choices, color codes, and guidelines for imagery. By providing clear instructions, we empowered GreenLeaf's internal teams 61 | and external partners to consistently represent the brand. 62 | </p> 63 | </div> 64 | <div class="col-span-full mt-12 grid grid-cols-2 gap-4 lg:grid-cols-4"> 65 | <div class="col-span-full grid grid-cols-2 gap-4 md:col-span-4 lg:grid-cols-3"> 66 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy3.png" alt="" /> 67 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy4.png" alt="" /> 68 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy5.png" alt="" /> 69 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy6.png" alt="" /> 70 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy7.png" alt="" /> 71 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy8.png" alt="" /> 72 | </div> 73 | <div class="col-span-full grid grid-cols-2 gap-4 md:col-span-4 lg:grid-cols-2"> 74 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy9.png" alt="" /> 75 | <img class="rounded-2xl lg:rounded-5xl" src="/casestudy10.png" alt="" /> 76 | </div> 77 | </div> 78 | <div class="prose-styles col-span-full mx-auto mt-12"> 79 | <h3>Results:</h3> 80 | <p> 81 | The transformation was met with resounding success. GreenLeaf Organic Grocers witnessed a significant positive response from both 82 | existing and new customers. The brand's fresh and sustainable image resonated with consumers seeking eco-conscious choices. The new 83 | brand identity also positioned GreenLeaf as a leader in the organic food industry. 84 | </p> 85 | 86 | <h3>Impact:</h3> 87 | <ul> 88 | <li> 89 | <strong>Increased Recognition:</strong> The revamped logo and visual elements led to higher brand recognition and recall among 90 | customers. 91 | </li> 92 | <li> 93 | <strong>Consistency:</strong> The implemented brand guidelines ensured a consistent and coherent brand representation across all 94 | platforms. 95 | </li> 96 | <li> 97 | <strong>Customer Engagement:</strong> The new brand image connected emotionally with customers who aligned with the sustainability 98 | ethos. 99 | </li> 100 | </ul> 101 | 102 | <h3>Conclusion:</h3> 103 | <p> 104 | Through the strategic marriage of design and strategy, we successfully transformed GreenLeaf Organic Grocers into a recognizable and 105 | admired brand. The collaboration between our team and GreenLeaf resulted in a brand identity that not only reflects their commitment 106 | to sustainability but also reinforces their position in the organic food market. 107 | </p> 108 | </div> 109 | </div> 110 | </section> 111 | -------------------------------------------------------------------------------- /src/components/work/WokCard.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from "astro:assets"; 3 | import { getLangFromUrl, useTranslations, useTranslatedPath } from "@i18n/utils"; 4 | 5 | const lang = getLangFromUrl(Astro.url); 6 | const t = useTranslations(lang); 7 | const translatePath = useTranslatedPath(lang); 8 | 9 | const { image, video, project, link, description, class: className } = Astro.props; 10 | --- 11 | 12 | <li class:list={["group flex flex-col", className]}> 13 | <a href={translatePath(link)} aria-label={`Project ${project}`}> 14 | { 15 | video != null ? ( 16 | <video class="aspect-[1.9/1] object-cover" src={video} loop muted playsinline autoplay /> 17 | ) : image ? ( 18 | <Image class="aspect-[1.9/1] w-full object-cover" src={image} alt={project} /> 19 | ) : ( 20 | <div>{project}</div> 21 | ) 22 | } 23 | </a> 24 | <div class="px-2 py-4"> 25 | <a href={translatePath(link)} class="flex items-baseline justify-between text-base font-medium lg:gap-x-8"> 26 | <h3 class="preview-project-title font-mono text-base font-medium uppercase tracking-wide md:mt-0"> 27 | {project} 28 | </h3> 29 | <span class="text-right text-base hover:text-primary-700 group-hover:animate-pulse-fast">■</span> 30 | </a> 31 | {description && <p class="mt-8 line-clamp-2 text-sm text-slate-500">{description}</p>} 32 | </div> 33 | </li> 34 | 35 | <script> 36 | import { gsap } from "gsap"; 37 | import { ScrollTrigger } from "gsap/ScrollTrigger"; 38 | import { ScrambleTextPlugin } from "gsap/ScrambleTextPlugin"; 39 | import { SplitText } from "gsap/SplitText"; 40 | 41 | gsap.registerPlugin(ScrollTrigger, ScrambleTextPlugin, SplitText); 42 | 43 | function init() { 44 | let projectTitles = gsap.utils.toArray(".preview-project-title") as HTMLElement[]; 45 | 46 | projectTitles.forEach((title) => { 47 | let split = new SplitText(title, { type: "words" }); 48 | let words = split.words; 49 | 50 | var scramblePreviewTitleTl = gsap 51 | .timeline({ 52 | defaults: { duration: 0.5, ease: "none" }, 53 | scrollTrigger: { 54 | trigger: title, 55 | start: "start bottom", 56 | end: "center 80%", 57 | toggleActions: "play none resume reset", 58 | markers: false, 59 | }, 60 | }) 61 | .to(words, { 62 | scrambleText: { 63 | text: "{original}", 64 | chars: "lowerCase", 65 | revealDelay: 0.5, 66 | speed: 0.5, 67 | tweenLength: false, 68 | }, 69 | }); 70 | }); 71 | } 72 | 73 | document.removeEventListener("DOMContentLoaded", init); // astro:page-load 74 | document.addEventListener("DOMContentLoaded", init); // astro:page-load 75 | </script> 76 | -------------------------------------------------------------------------------- /src/content/authors/en/charlie-foster.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Charlie Foster 3 | description: "Charlie Foster" 4 | image: 5 | source: "@assets/images/authors/charlie-foster.png" 6 | alt: "#" 7 | --- 8 | 9 | -------------------------------------------------------------------------------- /src/content/authors/it/charlie-foster.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Charlie Foster 3 | description: "Charlie Foster" 4 | image: 5 | source: "@assets/images/authors/charlie-foster.png" 6 | alt: "#" 7 | --- 8 | 9 | -------------------------------------------------------------------------------- /src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection, z } from "astro:content"; 2 | 3 | const postsCollection = defineCollection({ 4 | schema: ({ image }) => 5 | z.object({ 6 | title: z.string(), 7 | pubDate: z.date(), 8 | description: z.string(), 9 | link: z.optional(z.string()), 10 | video: z.optional(z.string()), 11 | author: z.object({ 12 | name: z.string(), 13 | link: z.string(), 14 | }), 15 | image: z.object({ 16 | source: image(), 17 | alt: z.string(), 18 | }), 19 | tags: z.array(z.string()), 20 | }), 21 | }); 22 | 23 | const projectsCollection = defineCollection({ 24 | schema: ({ image }) => 25 | z.object({ 26 | title: z.string(), 27 | pubDate: z.date(), 28 | description: z.string(), 29 | link: z.string(), 30 | author: z.object({ 31 | name: z.string(), 32 | link: z.string(), 33 | }), 34 | order: z.number(), 35 | video: z.optional(z.string()), 36 | image: z.optional( 37 | z.object({ 38 | source: image(), 39 | alt: z.string(), 40 | }), 41 | ), 42 | tags: z.optional(z.array(z.string())), 43 | }), 44 | }); 45 | 46 | const authorsCollection = defineCollection({ 47 | schema: ({ image }) => 48 | z.object({ 49 | name: z.string(), 50 | description: z.string(), 51 | image: z.object({ 52 | source: image(), 53 | alt: z.string(), 54 | }), 55 | }), 56 | }); 57 | 58 | export const collections = { 59 | posts: postsCollection, 60 | projects: projectsCollection, 61 | authors: authorsCollection, 62 | }; 63 | -------------------------------------------------------------------------------- /src/content/posts/en/ai-webdev-2024.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-02-15 3 | author: 4 | name: Alex Rivera 5 | link: /author/alex-rivera 6 | title: AI-Powered Web Development Trends in 2024 7 | description: "Explore how artificial intelligence is reshaping web development practices in 2024. From AI-assisted coding to intelligent testing, discover the tools and technologies driving the future of web development." 8 | image: 9 | source: "@assets/images/blog/ai-webdev-2024.jpg" 10 | alt: "Abstract visualization of AI neural networks in web development" 11 | tags: ["Artificial Intelligence", "Web Development", "Machine Learning", "Developer Tools", "Automation"] 12 | --- 13 | 14 | Artificial Intelligence is fundamentally changing how developers build and maintain web applications in 2024. From code generation to automated testing, AI tools are becoming an integral part of the modern development workflow. 15 | 16 | ## The AI Development Revolution 17 | 18 | The integration of AI in web development brings transformative capabilities: 19 | 20 | - Intelligent code completion and generation 21 | - Automated bug detection and fixing 22 | - Smart code review and optimization 23 | - Natural language to code conversion 24 | - AI-powered development assistants 25 | 26 | ## Essential AI Development Tools 27 | 28 | Today's AI-powered development landscape includes: 29 | 30 | - GitHub Copilot and similar code assistants 31 | - AI-driven testing frameworks 32 | - Automated code optimization tools 33 | - Machine learning-based security scanning 34 | - Intelligent debugging assistants 35 | 36 | ## Impact Across Development Phases 37 | 38 | AI is enhancing every stage of development: 39 | 40 | - Planning: Smart project estimation and resource allocation 41 | - Design: AI-generated UI/UX suggestions 42 | - Development: Automated code generation and optimization 43 | - Testing: Intelligent test case generation 44 | - Deployment: Smart infrastructure management 45 | 46 | ## Enhanced Developer Productivity 47 | 48 | AI tools are revolutionizing developer workflows: 49 | 50 | - Automated documentation generation 51 | - Intelligent code refactoring 52 | - Smart dependency management 53 | - Automated code review 54 | - Performance optimization suggestions 55 | 56 | ## Real-World Benefits 57 | 58 | Organizations implementing AI-powered development are experiencing: 59 | 60 | - 40% reduction in development time 61 | - 60% decrease in bug detection time 62 | - Improved code quality metrics 63 | - Enhanced developer satisfaction 64 | - Faster project delivery 65 | 66 | ## Security and AI 67 | 68 | AI is transforming security practices through: 69 | 70 | - Automated vulnerability detection 71 | - Intelligent threat monitoring 72 | - Smart access control systems 73 | - Predictive security analysis 74 | - Real-time threat response 75 | 76 | ## Future Perspectives 77 | 78 | As AI continues to evolve, we're witnessing the emergence of even more sophisticated development tools and practices. The combination of machine learning, natural language processing, and traditional development practices is creating a new paradigm for web application creation. 79 | 80 | Join us in exploring this exciting intersection of artificial intelligence and web development that's defining the future of our industry! 81 | -------------------------------------------------------------------------------- /src/content/posts/en/edge-computing-2025.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-02-15 3 | author: 4 | name: Alex Rivera 5 | link: /author/alex-rivera 6 | title: The Rise of Edge Computing in Modern Web Architecture 7 | description: "Discover how edge computing is transforming web development in 2024. From improved performance to enhanced security, learn why businesses are rapidly adopting edge computing solutions for their web applications." 8 | image: 9 | source: "@assets/images/blog/edge-computing-2024.jpg" 10 | alt: "Digital network visualization representing edge computing infrastructure" 11 | tags: ["Edge Computing", "Web Development", "Cloud Computing", "Performance", "Security"] 12 | --- 13 | 14 | Edge computing is revolutionizing how we build and deploy web applications in 2024. By bringing computation closer to data sources, organizations are achieving unprecedented levels of performance and reliability. 15 | 16 | ## The Evolution of Edge Computing 17 | 18 | The shift from traditional cloud computing to edge architecture brings several advantages: 19 | 20 | - Reduced latency and faster response times 21 | - Lower bandwidth costs 22 | - Enhanced data privacy and security 23 | - Improved reliability and redundancy 24 | - Better support for real-time applications 25 | 26 | ## Key Implementation Strategies 27 | 28 | Modern edge computing implementations focus on: 29 | 30 | - Distributed caching mechanisms 31 | - Edge-native security protocols 32 | - Serverless function deployment 33 | - Content delivery optimization 34 | - Real-time data processing 35 | 36 | ## Industry Applications 37 | 38 | Edge computing is transforming various sectors: 39 | 40 | - E-commerce: Real-time inventory and pricing updates 41 | - Healthcare: Remote patient monitoring 42 | - Gaming: Low-latency multiplayer experiences 43 | - IoT: Efficient device management 44 | - Financial Services: Secure transaction processing 45 | 46 | ## Security Considerations 47 | 48 | Edge computing introduces new security paradigms: 49 | 50 | - Zero-trust architecture implementation 51 | - Edge-specific encryption protocols 52 | - Distributed authentication systems 53 | - Real-time threat detection 54 | - Automated security responses 55 | 56 | ## Performance Benefits 57 | 58 | Organizations implementing edge computing are seeing: 59 | 60 | - 50-70% reduction in latency 61 | - 30-40% decrease in bandwidth costs 62 | - Improved application reliability 63 | - Better user experiences 64 | - Enhanced global scalability 65 | 66 | ## Looking Ahead 67 | 68 | As edge computing continues to mature, we're seeing the emergence of new patterns and practices that will define the future of web architecture. The combination of 5G networks, IoT proliferation, and edge computing is creating a new paradigm for web application development. 69 | 70 | Stay tuned as we continue to explore this exciting technological frontier that's reshaping the web development landscape! 71 | -------------------------------------------------------------------------------- /src/content/posts/en/game-changing-mobile-development-trends-in-2025.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-02-15 3 | author: 4 | name: Alex Rivera 5 | link: /author/alex-rivera 6 | title: Game-Changing Mobile Development Trends in 2024 7 | description: "Discover the revolutionary mobile development trends transforming app creation in 2024. From cross-platform solutions to emerging technologies, learn what's driving the future of mobile applications." 8 | image: 9 | source: "@assets/images/blog/mobile-development-trends-2024.jpg" 10 | alt: "Abstract visualization of mobile development concepts and technologies" 11 | tags: ["Mobile Development", "Technology", "Innovation", "Cross-Platform", "Mobile Apps"] 12 | --- 13 | 14 | The mobile development ecosystem is experiencing unprecedented transformation in 2024. As smartphones become increasingly powerful, developers are pushing the boundaries of what's possible in mobile applications. 15 | 16 | ## Cross-Platform Development Revolution 17 | 18 | The battle between native and cross-platform development is reaching new heights. Framework innovations are enabling developers to create high-performance apps that truly feel native while maintaining a single codebase. This shift is dramatically reducing development time and costs. 19 | 20 | ## Augmented Reality Takes Center Stage 21 | 22 | AR is no longer confined to gaming and entertainment. Mobile developers are incorporating AR features into: 23 | 24 | - Shopping experiences 25 | - Educational applications 26 | - Navigation systems 27 | - Industrial training tools 28 | 29 | ## Edge Computing Integration 30 | 31 | Edge computing is revolutionizing mobile app performance by bringing processing closer to the user. Benefits include: 32 | 33 | - Reduced latency 34 | - Enhanced privacy 35 | - Better offline functionality 36 | - Improved battery efficiency 37 | 38 | ## Sustainable Development Practices 39 | 40 | Environmental consciousness is influencing mobile development, with focus on: 41 | 42 | - Energy-efficient algorithms 43 | - Sustainable hosting solutions 44 | - Carbon-aware computing 45 | - Optimized battery consumption 46 | 47 | ## Looking Ahead 48 | 49 | The mobile development landscape continues to evolve at breakneck speed. These trends represent the beginning of a new era in mobile computing, where performance, sustainability, and user experience converge to create unprecedented possibilities. 50 | 51 | Stay connected for more updates on the transformative world of mobile development! 52 | -------------------------------------------------------------------------------- /src/content/posts/en/web-development-trends-2025.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-01-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Web Development Trends in 2024 7 | description: "Exploring the most exciting web development trends that are shaping the digital landscape in 2024. From AI integration to advanced frameworks, discover what's driving innovation in web development." 8 | image: 9 | source: "@assets/images/blog/web-development-trends-2024.jpg" 10 | alt: "Illustration showing various web development technologies and trends" 11 | tags: ["Web Development", "Technology", "Trends", "AI", "Frontend"] 12 | --- 13 | 14 | The web development landscape is constantly evolving, and 2024 brings exciting new possibilities to the forefront. As we navigate through this dynamic field, several key trends are emerging that promise to reshape how we build and interact with web applications. 15 | 16 | ## AI Integration in Web Development 17 | 18 | Artificial Intelligence is no longer just a buzzword – it's becoming an integral part of web development. From AI-powered code completion to intelligent debugging tools, developers are leveraging these technologies to streamline their workflow and create more sophisticated applications. 19 | 20 | ## The Rise of Web Components 21 | 22 | Web Components are gaining significant traction, offering a standardized way to create reusable custom elements. This approach to component-based architecture is making it easier to build maintainable and scalable applications while ensuring better compatibility across different frameworks. 23 | 24 | ## Performance-First Development 25 | 26 | With Core Web Vitals becoming increasingly important for SEO and user experience, developers are placing greater emphasis on performance optimization. This includes: 27 | 28 | - Implementing efficient loading strategies 29 | - Optimizing asset delivery 30 | - Utilizing modern image formats 31 | - Adopting serverless architectures 32 | 33 | ## Enhanced Security Measures 34 | 35 | As cyber threats continue to evolve, security has become a top priority. We're seeing increased adoption of: 36 | 37 | - Zero-trust security models 38 | - Advanced authentication methods 39 | - Real-time threat detection 40 | - Automated security testing 41 | 42 | ## The Future is Exciting 43 | 44 | These trends represent just a fraction of the innovations happening in web development. As we continue through 2024, we'll likely see even more exciting developments that push the boundaries of what's possible on the web. 45 | 46 | Stay tuned for more insights into the ever-evolving world of web development! 47 | -------------------------------------------------------------------------------- /src/content/posts/it/ai-webdev-2024.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-02-15 3 | author: 4 | name: Alex Rivera 5 | link: /author/alex-rivera 6 | title: AI-Powered Web Development Trends in 2024 7 | description: "Explore how artificial intelligence is reshaping web development practices in 2024. From AI-assisted coding to intelligent testing, discover the tools and technologies driving the future of web development." 8 | image: 9 | source: "@assets/images/blog/ai-webdev-2024.jpg" 10 | alt: "Abstract visualization of AI neural networks in web development" 11 | tags: ["Artificial Intelligence", "Web Development", "Machine Learning", "Developer Tools", "Automation"] 12 | --- 13 | 14 | Artificial Intelligence is fundamentally changing how developers build and maintain web applications in 2024. From code generation to automated testing, AI tools are becoming an integral part of the modern development workflow. 15 | 16 | ## The AI Development Revolution 17 | 18 | The integration of AI in web development brings transformative capabilities: 19 | 20 | - Intelligent code completion and generation 21 | - Automated bug detection and fixing 22 | - Smart code review and optimization 23 | - Natural language to code conversion 24 | - AI-powered development assistants 25 | 26 | ## Essential AI Development Tools 27 | 28 | Today's AI-powered development landscape includes: 29 | 30 | - GitHub Copilot and similar code assistants 31 | - AI-driven testing frameworks 32 | - Automated code optimization tools 33 | - Machine learning-based security scanning 34 | - Intelligent debugging assistants 35 | 36 | ## Impact Across Development Phases 37 | 38 | AI is enhancing every stage of development: 39 | 40 | - Planning: Smart project estimation and resource allocation 41 | - Design: AI-generated UI/UX suggestions 42 | - Development: Automated code generation and optimization 43 | - Testing: Intelligent test case generation 44 | - Deployment: Smart infrastructure management 45 | 46 | ## Enhanced Developer Productivity 47 | 48 | AI tools are revolutionizing developer workflows: 49 | 50 | - Automated documentation generation 51 | - Intelligent code refactoring 52 | - Smart dependency management 53 | - Automated code review 54 | - Performance optimization suggestions 55 | 56 | ## Real-World Benefits 57 | 58 | Organizations implementing AI-powered development are experiencing: 59 | 60 | - 40% reduction in development time 61 | - 60% decrease in bug detection time 62 | - Improved code quality metrics 63 | - Enhanced developer satisfaction 64 | - Faster project delivery 65 | 66 | ## Security and AI 67 | 68 | AI is transforming security practices through: 69 | 70 | - Automated vulnerability detection 71 | - Intelligent threat monitoring 72 | - Smart access control systems 73 | - Predictive security analysis 74 | - Real-time threat response 75 | 76 | ## Future Perspectives 77 | 78 | As AI continues to evolve, we're witnessing the emergence of even more sophisticated development tools and practices. The combination of machine learning, natural language processing, and traditional development practices is creating a new paradigm for web application creation. 79 | 80 | Join us in exploring this exciting intersection of artificial intelligence and web development that's defining the future of our industry! 81 | -------------------------------------------------------------------------------- /src/content/posts/it/edge-computing-2024.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-02-15 3 | author: 4 | name: Alex Rivera 5 | link: /author/alex-rivera 6 | title: The Rise of Edge Computing in Modern Web Architecture 7 | description: "Discover how edge computing is transforming web development in 2024. From improved performance to enhanced security, learn why businesses are rapidly adopting edge computing solutions for their web applications." 8 | image: 9 | source: "@assets/images/blog/edge-computing-2024.jpg" 10 | alt: "Digital network visualization representing edge computing infrastructure" 11 | tags: ["Edge Computing", "Web Development", "Cloud Computing", "Performance", "Security"] 12 | --- 13 | 14 | Edge computing is revolutionizing how we build and deploy web applications in 2024. By bringing computation closer to data sources, organizations are achieving unprecedented levels of performance and reliability. 15 | 16 | ## The Evolution of Edge Computing 17 | 18 | The shift from traditional cloud computing to edge architecture brings several advantages: 19 | 20 | - Reduced latency and faster response times 21 | - Lower bandwidth costs 22 | - Enhanced data privacy and security 23 | - Improved reliability and redundancy 24 | - Better support for real-time applications 25 | 26 | ## Key Implementation Strategies 27 | 28 | Modern edge computing implementations focus on: 29 | 30 | - Distributed caching mechanisms 31 | - Edge-native security protocols 32 | - Serverless function deployment 33 | - Content delivery optimization 34 | - Real-time data processing 35 | 36 | ## Industry Applications 37 | 38 | Edge computing is transforming various sectors: 39 | 40 | - E-commerce: Real-time inventory and pricing updates 41 | - Healthcare: Remote patient monitoring 42 | - Gaming: Low-latency multiplayer experiences 43 | - IoT: Efficient device management 44 | - Financial Services: Secure transaction processing 45 | 46 | ## Security Considerations 47 | 48 | Edge computing introduces new security paradigms: 49 | 50 | - Zero-trust architecture implementation 51 | - Edge-specific encryption protocols 52 | - Distributed authentication systems 53 | - Real-time threat detection 54 | - Automated security responses 55 | 56 | ## Performance Benefits 57 | 58 | Organizations implementing edge computing are seeing: 59 | 60 | - 50-70% reduction in latency 61 | - 30-40% decrease in bandwidth costs 62 | - Improved application reliability 63 | - Better user experiences 64 | - Enhanced global scalability 65 | 66 | ## Looking Ahead 67 | 68 | As edge computing continues to mature, we're seeing the emergence of new patterns and practices that will define the future of web architecture. The combination of 5G networks, IoT proliferation, and edge computing is creating a new paradigm for web application development. 69 | 70 | Stay tuned as we continue to explore this exciting technological frontier that's reshaping the web development landscape! 71 | -------------------------------------------------------------------------------- /src/content/posts/it/game-changing-mobile-development-trends-in-2024.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-02-15 3 | author: 4 | name: Alex Rivera 5 | link: /author/alex-rivera 6 | title: Game-Changing Mobile Development Trends in 2024 7 | description: "Discover the revolutionary mobile development trends transforming app creation in 2024. From cross-platform solutions to emerging technologies, learn what's driving the future of mobile applications." 8 | image: 9 | source: "@assets/images/blog/mobile-development-trends-2024.jpg" 10 | alt: "Abstract visualization of mobile development concepts and technologies" 11 | tags: ["Mobile Development", "Technology", "Innovation", "Cross-Platform", "Mobile Apps"] 12 | --- 13 | 14 | The mobile development ecosystem is experiencing unprecedented transformation in 2024. As smartphones become increasingly powerful, developers are pushing the boundaries of what's possible in mobile applications. 15 | 16 | ## Cross-Platform Development Revolution 17 | 18 | The battle between native and cross-platform development is reaching new heights. Framework innovations are enabling developers to create high-performance apps that truly feel native while maintaining a single codebase. This shift is dramatically reducing development time and costs. 19 | 20 | ## Augmented Reality Takes Center Stage 21 | 22 | AR is no longer confined to gaming and entertainment. Mobile developers are incorporating AR features into: 23 | 24 | - Shopping experiences 25 | - Educational applications 26 | - Navigation systems 27 | - Industrial training tools 28 | 29 | ## Edge Computing Integration 30 | 31 | Edge computing is revolutionizing mobile app performance by bringing processing closer to the user. Benefits include: 32 | 33 | - Reduced latency 34 | - Enhanced privacy 35 | - Better offline functionality 36 | - Improved battery efficiency 37 | 38 | ## Sustainable Development Practices 39 | 40 | Environmental consciousness is influencing mobile development, with focus on: 41 | 42 | - Energy-efficient algorithms 43 | - Sustainable hosting solutions 44 | - Carbon-aware computing 45 | - Optimized battery consumption 46 | 47 | ## Looking Ahead 48 | 49 | The mobile development landscape continues to evolve at breakneck speed. These trends represent the beginning of a new era in mobile computing, where performance, sustainability, and user experience converge to create unprecedented possibilities. 50 | 51 | Stay connected for more updates on the transformative world of mobile development! 52 | -------------------------------------------------------------------------------- /src/content/posts/it/web-development-trends-2024.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2024-01-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Web Development Trends in 2024 7 | description: "Exploring the most exciting web development trends that are shaping the digital landscape in 2024. From AI integration to advanced frameworks, discover what's driving innovation in web development." 8 | image: 9 | source: "@assets/images/blog/web-development-trends-2024.jpg" 10 | alt: "Illustration showing various web development technologies and trends" 11 | tags: ["Web Development", "Technology", "Trends", "AI", "Frontend"] 12 | --- 13 | 14 | The web development landscape is constantly evolving, and 2024 brings exciting new possibilities to the forefront. As we navigate through this dynamic field, several key trends are emerging that promise to reshape how we build and interact with web applications. 15 | 16 | ## AI Integration in Web Development 17 | 18 | Artificial Intelligence is no longer just a buzzword – it's becoming an integral part of web development. From AI-powered code completion to intelligent debugging tools, developers are leveraging these technologies to streamline their workflow and create more sophisticated applications. 19 | 20 | ## The Rise of Web Components 21 | 22 | Web Components are gaining significant traction, offering a standardized way to create reusable custom elements. This approach to component-based architecture is making it easier to build maintainable and scalable applications while ensuring better compatibility across different frameworks. 23 | 24 | ## Performance-First Development 25 | 26 | With Core Web Vitals becoming increasingly important for SEO and user experience, developers are placing greater emphasis on performance optimization. This includes: 27 | 28 | - Implementing efficient loading strategies 29 | - Optimizing asset delivery 30 | - Utilizing modern image formats 31 | - Adopting serverless architectures 32 | 33 | ## Enhanced Security Measures 34 | 35 | As cyber threats continue to evolve, security has become a top priority. We're seeing increased adoption of: 36 | 37 | - Zero-trust security models 38 | - Advanced authentication methods 39 | - Real-time threat detection 40 | - Automated security testing 41 | 42 | ## The Future is Exciting 43 | 44 | These trends represent just a fraction of the innovations happening in web development. As we continue through 2024, we'll likely see even more exciting developments that push the boundaries of what's possible on the web. 45 | 46 | Stay tuned for more insights into the ever-evolving world of web development! 47 | -------------------------------------------------------------------------------- /src/content/projects/en/astroagency.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Astroagency 7 | description: "Astroagency" 8 | link: "https://astroagency.majestico.co" 9 | video: /projects/astroagency/astroagency.webm 10 | order: 3 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/en/astros.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Astros 7 | description: "Astros" 8 | link: "https://astros.zank.studio" 9 | video: /projects/astros/astros.webm 10 | order: 2 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/en/comingsoon.1.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Coming soon 7 | description: "Coming soon." 8 | link: "#" 9 | video: /projects/comingsoon/comingsoon.webm 10 | order: 7 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/en/comingsoon.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Coming soon 7 | description: "Coming soon." 8 | link: "#" 9 | video: /projects/comingsoon/comingsoon.webm 10 | order: 7 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/en/flexfolio.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Flexfolio 7 | description: "Flexfolio" 8 | link: "https://flexfolio.zank.studio" 9 | video: /projects/flexfolio/flexfolio.webm 10 | order: 4 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/it/astroagency.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Astroagency 7 | description: "Astroagency" 8 | link: "https://astroagency.majestico.co" 9 | video: /projects/astroagency/astroagency.webm 10 | order: 3 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/it/astros.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Astros 7 | description: "Astros" 8 | link: "https://astros.zank.studio" 9 | video: /projects/astros/astros.webm 10 | order: 2 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/it/comingsoon.1.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Coming soon 7 | description: "Coming soon." 8 | link: "#" 9 | video: /projects/comingsoon/comingsoon.webm 10 | order: 7 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/it/comingsoon.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Coming soon 7 | description: "Coming soon." 8 | link: "#" 9 | video: /projects/comingsoon/comingsoon.webm 10 | order: 7 11 | --- 12 | -------------------------------------------------------------------------------- /src/content/projects/it/flexfolio.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | pubDate: 2023-07-01 3 | author: 4 | name: Charlie Foster 5 | link: /author/charlie-foster 6 | title: Flexfolio 7 | description: "Flexfolio" 8 | link: "https://flexfolio.zank.studio" 9 | video: /projects/flexfolio/flexfolio.webm 10 | order: 4 11 | --- 12 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference path="../.astro/types.d.ts" /> 2 | /// <reference types="astro/client" /> 3 | -------------------------------------------------------------------------------- /src/i18n/ui.ts: -------------------------------------------------------------------------------- 1 | export const languages = { 2 | en: "English", 3 | it: "Italiano", 4 | }; 5 | 6 | export const defaultLang = "en"; 7 | 8 | export const ui = { 9 | en: { 10 | or: "or", 11 | contact: "Contact", 12 | contacts: "Contacts", 13 | contactus: "Contact us", 14 | projects: "Projects", 15 | services: "Services", 16 | homepage: "Homepage", 17 | tagline: 18 | "Weaving digital dreams into reality. Where innovation dances with imagination, and every pixel tells a story of boundless creativity.", 19 | "projects.yours": "Your project", 20 | "projects.see": "See project", 21 | "hero.title.main": "We shape", 22 | "hero.title.1": "visions", 23 | "hero.title.2": "dreams", 24 | "hero.title.3": "worlds", 25 | "hero.title.4": "futures", 26 | "hero.subtitle": 27 | "Conjuring digital realms where imagination takes flight and possibilities know no bounds", 28 | "hero.scroll": "scroll down to explore", 29 | "websites.title": "Websites", 30 | "apps.title": "Apps", 31 | "uiux.title": "UI/UX Design", 32 | "seo.title": "SEO", 33 | "advertising.title": "Advertising", 34 | "websites.content": 35 | "Sculpting digital landscapes where dreams take form. Our creations blend artistry with innovation, crafting spaces that inspire and transform.", 36 | "apps.content": 37 | "Forging digital companions that dance at your fingertips. We create tools that feel like magic, yet work like clockwork.", 38 | "uiux.content": 39 | "Painting experiences that feel like second nature. Every interaction is a brushstroke in our canvas of seamless digital journeys.", 40 | "seo.content": 41 | "Charting paths through the digital cosmos. We guide your story to those seeking its light, using data as our compass.", 42 | "advertising.content": 43 | "Crafting digital echoes that resonate across screens and hearts. We turn whispers into conversations that matter.", 44 | "privacy.wip": "Work in progress", 45 | "privacy.wip.content": "This page will be updated soon", 46 | "faqs.question1": "What's your creative process?", 47 | "faqs.answer1": 48 | "Every journey begins with a spark of imagination. We dive deep into your vision, craft prototypes of possibility, and iterate until magic happens.", 49 | "faqs.question2": "How do you approach digital crafting?", 50 | "faqs.answer2": 51 | "Each project is a unique constellation. We typically invest 4-6 weeks breathing life into your vision, with investments ranging from €3000 for digital gardens to €4500 for enchanted marketplaces.", 52 | "faqs.question3": "What about ongoing nurture?", 53 | "faqs.answer3": 54 | "Your digital space needs sustenance to thrive. We offer sanctuary in our cloud realms for a modest offering of $5-$10 monthly, though some magical spaces can exist free of earthly bonds.", 55 | "faqs.question4": "How long does the enchantment take?", 56 | "faqs.answer4": 57 | "We weave our digital tapestries over 6 weeks, though more complex spells may require additional time to perfect.", 58 | "faqs.question5": "What if the vision isn't aligned?", 59 | "faqs.answer5": 60 | "Before we begin our craft, we'll create a mirror of possibilities - a mockup reflecting your dreams. If it doesn't capture your essence, we'll start fresh until it resonates perfectly.", 61 | "faqs.question6": "Can I reshape the magic myself?", 62 | "faqs.answer6": 63 | "We'll empower you with the ability to weave new words and, in some cases, reshape the very fabric of your digital space.", 64 | "faqs.question7": "Where will this digital realm reside?", 65 | "faqs.answer7": 66 | "We'll guide you to establish your digital sanctuary, ensuring you hold the keys to your kingdom while we tend to its needs.", 67 | "faqs.question8": "What if I seek new guardians?", 68 | "faqs.answer8": 69 | "Your digital realm remains yours to command. We provide full sovereignty over your domain, including its sacred source code.", 70 | "faqs.question9": "Who crafts the stories within?", 71 | "faqs.answer9": 72 | "If you haven't yet written your digital tale, we'll gather to weave stories that capture your essence and craft them into reality.", 73 | "faqs.question10": "What mystical tools do you wield?", 74 | "faqs.answer10": 75 | "We choose our instruments based on the song your project wishes to sing. Astro for swift, light melodies; WooCommerce or Shopify for grand market symphonies.", 76 | "faq.otherquestions": "Other questions?", 77 | "thanks.subtitle": "Mail sent successfully", 78 | "thanks.title": "Thank you", 79 | "thanks.content": "We will get back to you as soon as possible", 80 | "contact.title": "Contact", 81 | "contact.subtitle": 82 | "Let's weave digital dreams together, one pixel at a time", 83 | "contact.name": "Name", 84 | "contact.email": "Email", 85 | "contact.message": "Message", 86 | "contact.company": "Company", 87 | "contact.agree": "By sending this, you agree to our", 88 | "contact.send": "Send", 89 | "blog.title": "Blog", 90 | "blog.subtitle": "Chronicles of Digital Craftsmanship", 91 | "blog.gotoproject": "Go to project", 92 | "footer.newsletter": "Subscribe to", 93 | "footer.newsletter2": "our newsletter", 94 | "footer.yourmail": "Your email", 95 | newsletter_subscribe: "Subscribe to our newsletter", 96 | }, 97 | it: { 98 | or: "o", 99 | contact: "Contattaci", 100 | contacts: "Contatti", 101 | contactus: "Contattaci", 102 | projects: "Progetti", 103 | services: "Servizi", 104 | homepage: "Homepage", 105 | tagline: 106 | "Intrecciamo sogni digitali in realtà. Dove l'innovazione danza con l'immaginazione, e ogni pixel racconta una storia di creatività senza confini.", 107 | "projects.yours": "Il tuo progetto", 108 | "projects.see": "Visualizza progetto", 109 | "hero.title.main": "Plasmiamo", 110 | "hero.title.1": "visioni", 111 | "hero.title.2": "sogni", 112 | "hero.title.3": "mondi", 113 | "hero.title.4": "futuri", 114 | "hero.subtitle": 115 | "Evochiamo regni digitali dove l'immaginazione prende il volo e le possibilità non conoscono limiti", 116 | "hero.scroll": "scorri per scoprire", 117 | "websites.title": "Siti", 118 | "apps.title": "App", 119 | "uiux.title": "Design UI/UX", 120 | "seo.title": "SEO", 121 | "advertising.title": "Pubblicità", 122 | "websites.content": 123 | "Scolpiamo paesaggi digitali dove i sogni prendono forma. Le nostre creazioni fondono arte e innovazione, creando spazi che ispirano e trasformano.", 124 | "apps.content": 125 | "Forgiamo compagni digitali che danzano sotto le tue dita. Creiamo strumenti che sembrano magia, ma funzionano come orologi.", 126 | "uiux.content": 127 | "Dipingiamo esperienze che sembrano naturali. Ogni interazione è una pennellata nella nostra tela di viaggi digitali senza soluzione di continuità.", 128 | "seo.content": 129 | "Tracciamo percorsi attraverso il cosmo digitale. Guidiamo la tua storia verso chi ne cerca la luce, usando i dati come bussola.", 130 | "advertising.content": 131 | "Creiamo echi digitali che risuonano attraverso schermi e cuori. Trasformiamo sussurri in conversazioni che contano.", 132 | "privacy.wip": "Lavori in corso", 133 | "privacy.wip.content": "Questa pagina verrà aggiornata a breve", 134 | "faqs.question1": "Qual è il vostro processo creativo?", 135 | "faqs.answer1": 136 | "Ogni viaggio inizia con una scintilla di immaginazione. Ci immergiamo nella tua visione, creiamo prototipi di possibilità e iteriamo fino a quando non accade la magia.", 137 | "faqs.question2": "Come approcciate la creazione digitale?", 138 | "faqs.answer2": 139 | "Ogni progetto è una costellazione unica. Investiamo tipicamente 4-6 settimane per dar vita alla tua visione, con investimenti da €3000 per giardini digitali a €4500 per mercati incantati.", 140 | "faqs.question3": "E riguardo alla cura continua?", 141 | "faqs.answer3": 142 | "Il tuo spazio digitale necessita di nutrimento per prosperare. Offriamo santuario nei nostri reami cloud per una modesta offerta di $5-$10 mensili, anche se alcuni spazi magici possono esistere liberi da vincoli terreni.", 143 | "faqs.question4": "Quanto dura l'incantesimo?", 144 | "faqs.answer4": 145 | "Tessiamo le nostre tappezzerie digitali in 6 settimane, anche se incantesimi più complessi potrebbero richiedere tempo aggiuntivo per la perfezione.", 146 | "faqs.question5": "E se la visione non è allineata?", 147 | "faqs.answer5": 148 | "Prima di iniziare il nostro mestiere, creeremo uno specchio di possibilità - un mockup che riflette i tuoi sogni. Se non cattura la tua essenza, ricominceremo da capo fino a quando non risuonerà perfettamente.", 149 | "faqs.question6": "Posso rimodellare la magia da solo?", 150 | "faqs.answer6": 151 | "Ti daremo il potere di tessere nuove parole e, in alcuni casi, rimodellare il tessuto stesso del tuo spazio digitale.", 152 | "faqs.question7": "Dove risiederà questo regno digitale?", 153 | "faqs.answer7": 154 | "Ti guideremo a stabilire il tuo santuario digitale, assicurandoti che tu detenga le chiavi del tuo regno mentre noi ci prendiamo cura delle sue necessità.", 155 | "faqs.question8": "E se cercassi nuovi guardiani?", 156 | "faqs.answer8": 157 | "Il tuo regno digitale rimane tuo da comandare. Ti forniamo piena sovranità sul tuo dominio, incluso il suo codice sorgente sacro.", 158 | "faqs.question9": "Chi crea le storie all'interno?", 159 | "faqs.answer9": 160 | "Se non hai ancora scritto il tuo racconto digitale, ci riuniremo per tessere storie che catturino la tua essenza e le trasformeremo in realtà.", 161 | "faqs.question10": "Quali strumenti mistici utilizzate?", 162 | "faqs.answer10": 163 | "Scegliamo i nostri strumenti in base alla canzone che il tuo progetto desidera cantare. Astro per melodie veloci e leggere; WooCommerce o Shopify per grandi sinfonie di mercato.", 164 | "faq.otherquestions": "Altre domande?", 165 | "thanks.subtitle": "Mail inviata con successo", 166 | "thanks.title": "Grazie", 167 | "thanks.content": "Ti risponderemo il prima possibile", 168 | "contact.title": "Contatti", 169 | "contact.subtitle": "Tessiamo insieme sogni digitali, un pixel alla volta", 170 | "contact.name": "Nome", 171 | "contact.email": "Email", 172 | "contact.message": "Messaggio", 173 | "contact.company": "Azienda", 174 | "contact.agree": "Cliccando invia accetti la nostra", 175 | "contact.send": "Invia", 176 | "blog.title": "Blog", 177 | "blog.subtitle": "Cronache dell'Artigianato Digitale", 178 | "blog.gotoproject": "Vai al progetto", 179 | "footer.newsletter": "Iscriviti alla", 180 | "footer.newsletter2": "newsletter", 181 | "footer.yourmail": "La tua email", 182 | newsletter_subscribe: "Iscriviti alla nostra newsletter", 183 | }, 184 | } as const; 185 | 186 | export const showDefaultLang = false; 187 | -------------------------------------------------------------------------------- /src/i18n/utils.ts: -------------------------------------------------------------------------------- 1 | import { ui, defaultLang, showDefaultLang } from "./ui"; 2 | 3 | export function getLangFromUrl(url: URL) { 4 | const [, lang] = url.pathname.split("/"); 5 | if (lang in ui) return lang as keyof typeof ui; 6 | return defaultLang; 7 | } 8 | 9 | export function getUrlWithoutLang(url: URL) { 10 | const [, langOrPath, ...pathLocale] = url.pathname.split("/"); 11 | 12 | if (langOrPath in ui) { 13 | return `/${pathLocale.join("/")}`; 14 | } else { 15 | return `/${langOrPath}${pathLocale.length ? "/" + pathLocale.join("/") : ""}`; 16 | } 17 | } 18 | 19 | export function useTranslations(lang: keyof typeof ui) { 20 | return function t(key: keyof (typeof ui)[typeof defaultLang]) { 21 | return ui[lang][key] || ui[defaultLang][key]; 22 | }; 23 | } 24 | 25 | export function useTranslatedPath(lang: keyof typeof ui) { 26 | return function translatePath(path: string, l: string = lang) { 27 | return !showDefaultLang && l === defaultLang ? path : `/${l}${path}`; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/layouts/BaseLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import type { AstroSeoProps } from "@astrolib/seo"; 3 | import BaseHead from "@components/BaseHead.astro"; 4 | import BackgroundLines from "@components/global/BackgroundLines.astro"; 5 | import Footer from "@components/global/Footer.astro"; 6 | import Navigation from "@components/global/Navigation.astro"; 7 | import { getLangFromUrl } from "../i18n/utils"; 8 | 9 | type Props = { 10 | seo?: AstroSeoProps; 11 | }; 12 | 13 | const { seo: seoProp } = Astro.props; 14 | 15 | const lang = getLangFromUrl(Astro.url); 16 | 17 | const shareImage = new URL("/images/meta/majestico_share.gif", Astro.site).toString(); 18 | 19 | const defaultSeo: AstroSeoProps = { 20 | title: "Web Agency Leading in Performant Web Design Solutions - Majestico Studio", 21 | description: 22 | "Majestico Studio excels in creating fast, user-friendly websites with a focus on aesthetic design and SEO optimization, ensuring a standout online presence", 23 | canonical: "https://majestico.co", 24 | openGraph: { 25 | url: "https://majestico.co", 26 | title: "Web Agency Leading in Performant Web Design Solutions - Majestico Studio", 27 | description: 28 | "Majestico Studio excels in creating fast, user-friendly websites with a focus on aesthetic design and SEO optimization, ensuring a standout online presence", 29 | images: [ 30 | { 31 | url: shareImage, 32 | width: 1200, 33 | height: 630, 34 | alt: "Social open graph", 35 | type: "image/gif", 36 | }, 37 | { 38 | url: shareImage, 39 | width: 4096, 40 | height: 4096, 41 | alt: "Twitter open graph", 42 | type: "image/gif", 43 | }, 44 | ], 45 | site_name: "Majestico", 46 | }, 47 | twitter: { 48 | handle: "@majesticostudio", 49 | site: "@majesticostudio", 50 | cardType: "summary_large_image", 51 | }, 52 | additionalMetaTags: [ 53 | { 54 | name: "publisher", 55 | content: "Charlie Foster", 56 | }, 57 | ], 58 | }; 59 | 60 | const seo = { 61 | ...defaultSeo, 62 | ...seoProp, 63 | }; 64 | --- 65 | 66 | <html lang={lang}> 67 | <head> 68 | <BaseHead seo={seo} /> 69 | </head> 70 | <body class="bg-black"> 71 | <Navigation /> 72 | <BackgroundLines /> 73 | <slot /> 74 | <Footer /> 75 | </body> 76 | </html> 77 | -------------------------------------------------------------------------------- /src/layouts/MarkdownPostLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from "astro:assets"; 3 | import type { CollectionEntry } from "astro:content"; 4 | import Cta from "@/components/blog/Cta.astro"; 5 | import type { AstroSeoProps } from "@astrolib/seo"; 6 | import Comments from "@components/blog/Comments.astro"; 7 | import Title from "@components/global/Title.astro"; 8 | import { getLangFromUrl, useTranslatedPath, useTranslations } from "@i18n/utils"; 9 | import { Icon } from "astro-icon/components"; 10 | import BaseLayout from "./BaseLayout.astro"; 11 | 12 | const lang = getLangFromUrl(Astro.url); 13 | const t = useTranslations(lang); 14 | const translatePath = useTranslatedPath(lang); 15 | 16 | type Props = { 17 | frontmatter: CollectionEntry<"projects"> | CollectionEntry<"posts">; 18 | body: string; 19 | seo?: AstroSeoProps; 20 | }; 21 | 22 | const { frontmatter, body, seo } = Astro.props; 23 | 24 | function getArticleReadingTime(body: string): number { 25 | if (!body) return 0; 26 | const wordsPerMinute = 183; 27 | const numberOfWords = body.split(/\s/g).length; 28 | const minutes = numberOfWords / wordsPerMinute; 29 | const readTime = Math.ceil(minutes); 30 | return readTime; 31 | } 32 | 33 | const readingTime = getArticleReadingTime(body); 34 | --- 35 | 36 | <BaseLayout seo={seo}> 37 | <section class="section"> 38 | <div class="relative z-20 col-span-full col-start-1 mx-auto max-w-7xl bg-black px-8 py-12 lg:col-span-2 lg:col-start-2 lg:py-32"> 39 | <div class="flex flex-col gap-6 pt-20"> 40 | <Title 41 | title={frontmatter.data.title} 42 | center={false} 43 | subtitle={`Written by: <a href="${frontmatter.data.author.link}" rel="author">${frontmatter.data.author.name}</a> on ${frontmatter.data.pubDate 44 | .toString() 45 | .slice(0, 10)}`} 46 | /> 47 | 48 | <p class="max-w-2xl text-base text-slate-400"> 49 | <em>{frontmatter.data.description}</em> 50 | </p> 51 | 52 | { 53 | frontmatter.data?.link && ( 54 | <a class="font-mono text-xl font-bold" href={frontmatter.data?.link} target="_blank"> 55 | <Icon name="iconamoon:link-external-thin" class="inline" /> {t("blog.gotoproject")} 56 | </a> 57 | ) 58 | } 59 | </div> 60 | <div class="mt-12 w-full justify-center"> 61 | { 62 | frontmatter.data.image != null ? ( 63 | <Image class="mt-12" src={frontmatter.data.image?.source} alt={frontmatter.data.image.alt} /> 64 | ) : frontmatter.data.video ? ( 65 | <video src={frontmatter.data.video} loop muted playsinline autoplay /> 66 | ) : ( 67 | <div>{frontmatter.data.title}</div> 68 | ) 69 | } 70 | <div class="flex justify-between px-2 pt-4 font-mono text-sm"> 71 | <div class="flex flex-wrap gap-1"> 72 | {(frontmatter.data?.tags?.length ?? 0) > 0 && <span class="py-3">TAGS:</span>} 73 | <ul class="flex flex-wrap justify-center"> 74 | { 75 | frontmatter?.data.tags?.map((tag: string) => ( 76 | <li class="inline-flex items-center rounded-full px-1 py-3 font-medium uppercase tracking-widest"> 77 | <a href={`/tags/${tag}`}>{tag}</a> 78 | </li> 79 | )) 80 | } 81 | </ul> 82 | </div> 83 | <span class="py-3">~{readingTime} MIN</span> 84 | </div> 85 | 86 | <div class="prose-styles py-12"><slot /></div> 87 | </div> 88 | <Comments /> 89 | </div> 90 | </section> 91 | <Cta /> 92 | </BaseLayout> 93 | -------------------------------------------------------------------------------- /src/pages/404.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Error404 from "@/components/infopages/Error404.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "404 Error: Page Not Found - Majestico Studio", 8 | description: 9 | "Oops! The page you're looking for doesn't exist. Navigate back to Majestico Studio's homepage for our full range of web design and SEO services.", 10 | canonical: "https://majestico.co/404/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <Error404 /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/author/[author].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | import Author from "@components/blog/Author.astro"; 4 | import { getCollection } from "astro:content"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | export async function getStaticPaths() { 8 | const authors = await getCollection("authors"); 9 | 10 | return authors 11 | .map((entry) => { 12 | const [lang, ...slug] = entry.slug.split("/"); 13 | return { params: { lang, author: slug.join("/") || undefined }, props: entry }; 14 | }) 15 | .filter((entry) => entry.params.lang === "en"); 16 | } 17 | 18 | const author = Astro.props; 19 | 20 | const seo: AstroSeoProps = { 21 | title: author.data.name, 22 | description: author.data.description, 23 | canonical: `https://majestico.co/author/${author.slug}/`, 24 | }; 25 | --- 26 | 27 | <BaseLayout seo={seo}> 28 | <Author author={author} /> 29 | </BaseLayout> 30 | -------------------------------------------------------------------------------- /src/pages/author/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import BaseLayout from "@layouts/BaseLayout.astro"; 4 | import Authors from "@components/blog/Authors.astro"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | const allAuthors = await getCollection("authors"); 8 | const pageTitle = "Tag Index"; 9 | 10 | const seo: AstroSeoProps = { 11 | title: "Authors - Majestico Studio: Meet Our Expert Team", 12 | description: 13 | "Discover the creative minds behind Majestico Studio. Our authors are industry experts in web design, SEO, and digital marketing, sharing valuable insights.", 14 | canonical: "https://majestico.co/author/", 15 | }; 16 | --- 17 | 18 | <BaseLayout seo={seo}> 19 | <Authors /> 20 | </BaseLayout> 21 | -------------------------------------------------------------------------------- /src/pages/blog/[...slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import MarkdownPostLayout from "../../layouts/MarkdownPostLayout.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | export async function getStaticPaths() { 7 | const blogEntries = await getCollection("posts"); 8 | 9 | return blogEntries 10 | .map((entry) => { 11 | const [lang, ...slug] = entry.slug.split("/"); 12 | return { params: { lang, slug: slug.join("/") || undefined }, props: entry }; 13 | }) 14 | .filter((entry) => entry.params.lang === "en"); 15 | } 16 | 17 | const entry = Astro.props; 18 | const { Content } = await entry.render(); 19 | 20 | const seo: AstroSeoProps = { 21 | title: entry.data.title, 22 | description: entry.data.description, 23 | canonical: `https://majestico.co/blog/${entry.slug}/`, 24 | openGraph: { 25 | url: entry.slug, 26 | title: entry.data.title, 27 | description: "{frontmatter.description}", 28 | images: [ 29 | { 30 | url: "https://majestico.co/opengraph/social-og.jpg", 31 | width: 1200, 32 | height: 630, 33 | alt: "Social open graph", 34 | type: "image/jpeg", 35 | }, 36 | { 37 | url: "https://majestico.co/opengraph/twitter-og.jpg", 38 | width: 4096, 39 | height: 4096, 40 | alt: "Twitter open graph", 41 | type: "image/jpeg", 42 | }, 43 | { url: "https://majestico.co/opengraph/social-og.jpg" }, 44 | { url: "https://majestico.co/opengraph/twitter-og.jpg" }, 45 | ], 46 | site_name: "YourSitesName", 47 | }, 48 | twitter: { 49 | handle: "@TwitterHandle", 50 | site: "@site", 51 | cardType: "summary_large_image", 52 | }, 53 | }; 54 | --- 55 | 56 | <MarkdownPostLayout frontmatter={entry} body={entry.body} seo={seo}> 57 | <Content /> 58 | </MarkdownPostLayout> 59 | -------------------------------------------------------------------------------- /src/pages/blog/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Articles from "@components/blog/Articles.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Majestico Studio Blog: Insights and Updates in Web Design, Web dev and SEO", 8 | description: 9 | "Explore the Majestico Studio Blog for the latest trends, tips, and insights in web design, web dev and SEO. Stay updated with our innovative digital solutions.", 10 | canonical: "https://majestico.co/blog/", 11 | openGraph: { 12 | url: "https://majestico.co/blog", 13 | title: "Majestico Studio Blog: Insights and Updates in Web Design, Web dev and SEO", 14 | description: 15 | "Stay informed with the latest in web design and SEO on the Majestico Studio Blog. Expert advice, tips, and industry insights await you.", 16 | images: [ 17 | { 18 | url: "https://majestico.co/opengraph/social-og.jpg", 19 | width: 1200, 20 | height: 630, 21 | alt: "Social open graph image of Majestico Studio Blog", 22 | type: "image/jpeg", 23 | }, 24 | { 25 | url: "https://majestico.co/opengraph/twitter-og.jpg", 26 | width: 4096, 27 | height: 4096, 28 | alt: "Twitter open graph image of Majestico Studio Blog", 29 | type: "image/jpeg", 30 | }, 31 | { url: "https://majestico.co/opengraph/social-og.jpg" }, 32 | { url: "https://majestico.co/opengraph/twitter-og.jpg" }, 33 | ], 34 | site_name: "Majestico Studio", 35 | }, 36 | twitter: { 37 | handle: "@majesticostudio", 38 | site: "@majesticostudio", 39 | cardType: "summary_large_image", 40 | }, 41 | }; 42 | --- 43 | 44 | <BaseLayout seo={seo}> 45 | <Articles /> 46 | </BaseLayout> 47 | -------------------------------------------------------------------------------- /src/pages/contact.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Contact from "@/components/forms/Contact.astro"; 4 | import Cta from "@components/blog/Cta.astro"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | const seo: AstroSeoProps = { 8 | title: "Contact Majestico Studio: Get in Touch for Innovative Web Solutions", 9 | description: 10 | "Reach out to Majestico Studio for expert web design and SEO services. Connect with us to discuss your project and explore cutting-edge web solutions.", 11 | canonical: "https://majestico.co/contact/", 12 | }; 13 | --- 14 | 15 | <BaseLayout seo={seo}> 16 | <Contact /> 17 | <Cta class="col-span-full col-start-1 row-start-3" /> 18 | </BaseLayout> 19 | -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Hero from "@components/landing/Hero.astro"; 4 | import Description from "@components/landing/Description.astro"; 5 | import WorkPreview from "@components/landing/WorkPreview.astro"; 6 | --- 7 | 8 | <BaseLayout> 9 | <Hero /> 10 | <Description /> 11 | <WorkPreview class="pb-80 sm:pb-40" /> 12 | </BaseLayout> 13 | -------------------------------------------------------------------------------- /src/pages/it/404.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Error404 from "@/components/infopages/Error404.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "404 Error: Page Not Found - Majestico Studio", 8 | description: 9 | "Oops! The page you're looking for doesn't exist. Navigate back to Majestico Studio's homepage for our full range of web design and SEO services.", 10 | canonical: "https://majestico.co/it/404/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <Error404 /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/it/author/[author].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | import Author from "@components/blog/Author.astro"; 4 | import { getCollection } from "astro:content"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | export async function getStaticPaths() { 8 | const authors = await getCollection("authors"); 9 | 10 | return authors 11 | .map((entry) => { 12 | const [lang, ...slug] = entry.slug.split("/"); 13 | return { params: { lang, author: slug.join("/") || undefined }, props: entry }; 14 | }) 15 | .filter((entry) => entry.params.lang === "it"); 16 | } 17 | 18 | const author = Astro.props; 19 | 20 | const seo: AstroSeoProps = { 21 | title: author.data.name, 22 | description: author.data.description, 23 | canonical: `https://majestico.co/it/author/${author.slug}/`, 24 | }; 25 | --- 26 | 27 | <BaseLayout seo={seo}> 28 | <Author author={author} /> 29 | </BaseLayout> 30 | -------------------------------------------------------------------------------- /src/pages/it/author/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import BaseLayout from "@layouts/BaseLayout.astro"; 4 | import Authors from "@components/blog/Authors.astro"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | const allAuthors = await getCollection("authors"); 8 | const pageTitle = "Tag Index"; 9 | 10 | const seo: AstroSeoProps = { 11 | title: "Authors - Majestico Studio: Meet Our Expert Team", 12 | description: 13 | "Discover the creative minds behind Majestico Studio. Our authors are industry experts in web design, SEO, and digital marketing, sharing valuable insights.", 14 | canonical: "https://majestico.co/it/author/", 15 | }; 16 | --- 17 | 18 | <BaseLayout seo={seo}> 19 | <Authors /> 20 | </BaseLayout> 21 | -------------------------------------------------------------------------------- /src/pages/it/blog/[...slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import MarkdownPostLayout from "@layouts/MarkdownPostLayout.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | export async function getStaticPaths() { 7 | const blogEntries = await getCollection("posts"); 8 | 9 | return blogEntries 10 | .map((entry) => { 11 | const [lang, ...slug] = entry.slug.split("/"); 12 | return { params: { lang, slug: slug.join("/") || undefined }, props: entry }; 13 | }) 14 | .filter((entry) => entry.params.lang === "it"); 15 | } 16 | 17 | const entry = Astro.props; 18 | const { Content } = await entry.render(); 19 | 20 | const seo: AstroSeoProps = { 21 | title: entry.data.title, 22 | description: entry.data.description, 23 | canonical: `https://majestico.co/it/blog/${entry.slug}/`, 24 | openGraph: { 25 | url: entry.slug, 26 | title: entry.data.title, 27 | description: "{frontmatter.description}", 28 | images: [ 29 | { 30 | url: "https://majestico.co/opengraph/social-og.jpg", 31 | width: 1200, 32 | height: 630, 33 | alt: "Social open graph", 34 | type: "image/jpeg", 35 | }, 36 | { 37 | url: "https://majestico.co/opengraph/twitter-og.jpg", 38 | width: 4096, 39 | height: 4096, 40 | alt: "Twitter open graph", 41 | type: "image/jpeg", 42 | }, 43 | { url: "https://majestico.co/opengraph/social-og.jpg" }, 44 | { url: "https://majestico.co/opengraph/twitter-og.jpg" }, 45 | ], 46 | site_name: "YourSitesName", 47 | }, 48 | twitter: { 49 | handle: "@TwitterHandle", 50 | site: "@site", 51 | cardType: "summary_large_image", 52 | }, 53 | }; 54 | --- 55 | 56 | <MarkdownPostLayout frontmatter={entry} body={entry.body} seo={seo}> 57 | <Content /> 58 | </MarkdownPostLayout> 59 | -------------------------------------------------------------------------------- /src/pages/it/blog/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | import Articles from "@components/blog/Articles.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Majestico Studio Blog: Insights and Updates in Web Design, Web dev and SEO", 8 | description: 9 | "Explore the Majestico Studio Blog for the latest trends, tips, and insights in web design, web dev and SEO. Stay updated with our innovative digital solutions.", 10 | canonical: "https://majestico.co/it/blog/", 11 | openGraph: { 12 | url: "https://majestico.co/blog", 13 | title: "Majestico Studio Blog: Insights and Updates in Web Design, Web dev and SEO", 14 | description: 15 | "Stay informed with the latest in web design and SEO on the Majestico Studio Blog. Expert advice, tips, and industry insights await you.", 16 | images: [ 17 | { 18 | url: "https://majestico.co/opengraph/social-og.jpg", 19 | width: 1200, 20 | height: 630, 21 | alt: "Social open graph image of Majestico Studio Blog", 22 | type: "image/jpeg", 23 | }, 24 | { 25 | url: "https://majestico.co/opengraph/twitter-og.jpg", 26 | width: 4096, 27 | height: 4096, 28 | alt: "Twitter open graph image of Majestico Studio Blog", 29 | type: "image/jpeg", 30 | }, 31 | { url: "https://majestico.co/opengraph/social-og.jpg" }, 32 | { url: "https://majestico.co/opengraph/twitter-og.jpg" }, 33 | ], 34 | site_name: "Majestico Studio", 35 | }, 36 | twitter: { 37 | handle: "@majesticostudio", 38 | site: "@majesticostudio", 39 | cardType: "summary_large_image", 40 | }, 41 | }; 42 | --- 43 | 44 | <BaseLayout seo={seo}> 45 | <Articles /> 46 | </BaseLayout> 47 | -------------------------------------------------------------------------------- /src/pages/it/contact.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Contact from "@/components/forms/Contact.astro"; 4 | import Cta from "@components/blog/Cta.astro"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | const seo: AstroSeoProps = { 8 | title: "Contact Majestico Studio: Get in Touch for Innovative Web Solutions", 9 | description: 10 | "Reach out to Majestico Studio for expert web design and SEO services. Connect with us to discuss your project and explore cutting-edge web solutions.", 11 | canonical: "https://majestico.co/it/contact/", 12 | }; 13 | --- 14 | 15 | <BaseLayout seo={seo}> 16 | <Contact /> 17 | <Cta class="col-span-full col-start-1 row-start-3" /> 18 | </BaseLayout> 19 | -------------------------------------------------------------------------------- /src/pages/it/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Hero from "@components/landing/Hero.astro"; 4 | import Description from "@components/landing/Description.astro"; 5 | import WorkPreview from "@components/landing/WorkPreview.astro"; 6 | --- 7 | 8 | <BaseLayout> 9 | <Hero /> 10 | <Description /> 11 | <WorkPreview class="pb-80 sm:pb-40" /> 12 | </BaseLayout> 13 | -------------------------------------------------------------------------------- /src/pages/it/privacy.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import PrivacyPage from "@/components/infopages/Privacy.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Privacy Policy - Majestico Studio: Your Privacy Matters", 8 | description: 9 | "Our Privacy Policy outlines how Majestico Studio protects your personal information. Discover our commitment to your privacy and data security.", 10 | canonical: "https://majestico.co/it/privacy/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <PrivacyPage /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/it/services/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import ServicesMain from "@/components/services/ServicesMain.astro"; 4 | 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | const seo: AstroSeoProps = { 8 | title: "Services | Majestico Studio", 9 | description: "We are a digital studio that creates beautiful and functional websites and applications.", 10 | canonical: "https://majestico.co/services/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <ServicesMain /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/it/tags/[tag].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | import Tag from "@components/blog/Tag.astro"; 4 | import { getCollection } from "astro:content"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | export async function getStaticPaths() { 8 | const allPosts = (await getCollection("posts")).filter((post) => { 9 | const [postLang, ...slug] = post.slug.split("/"); 10 | return postLang === "en"; 11 | }); 12 | 13 | const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())]; 14 | return uniqueTags.map((tag) => { 15 | const filteredPosts = allPosts.filter((post) => post.data.tags.includes(tag)); 16 | return { 17 | params: { tag }, 18 | props: { posts: filteredPosts }, 19 | }; 20 | }); 21 | } 22 | const { tag } = Astro.params; 23 | const { posts } = Astro.props; 24 | 25 | const seo: AstroSeoProps = { 26 | title: `${tag} | Majestico Studio`, 27 | description: `Explore articles and insights on '${tag}' at Majestico Studio. Dive into a wealth of knowledge covering the latest trends and tips in web design and SEO.`, 28 | canonical: `https://majestico.co/it/tags/${tag}/`, 29 | }; 30 | --- 31 | 32 | <BaseLayout seo={seo}> 33 | <Tag tag={tag} posts={posts} /> 34 | </BaseLayout> 35 | -------------------------------------------------------------------------------- /src/pages/it/tags/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import BaseLayout from "@layouts/BaseLayout.astro"; 4 | import Tags from "@components/blog/Tags.astro"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | const seo: AstroSeoProps = { 8 | title: "Tags - Explore Topics at Majestico Studio", 9 | description: 10 | "Browse through the diverse range of tags at Majestico Studio to find articles and insights tailored to your interests in web design and SEO.", 11 | canonical: "https://majestico.co/it/tags/", 12 | }; 13 | --- 14 | 15 | <BaseLayout seo={seo}> 16 | <Tags /> 17 | </BaseLayout> 18 | -------------------------------------------------------------------------------- /src/pages/it/terms.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import TermsPage from "@/components/infopages/Terms.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Terms of Service - Majestico Studio: Understand Our Terms", 8 | description: 9 | "Read the Terms of Service for Majestico Studio. Learn about our guidelines and your responsibilities when using our web design and SEO services.", 10 | canonical: "https://majestico.co/it/terms/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <TermsPage /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/it/thank-you.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Thanks from "@components/infopages/ThankYou.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Thank You - Majestico Studio: Email Successfully Sent", 8 | description: 9 | "Thank you for reaching out to Majestico Studio! Your email has been successfully sent. We appreciate your interest and will respond promptly.", 10 | canonical: "https://majestico.co/it/thank-you/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <Thanks /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/it/work/[...slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import MarkdownPostLayout from "@layouts/MarkdownPostLayout.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | export async function getStaticPaths() { 7 | const projectsEntries = await getCollection("projects"); 8 | 9 | return projectsEntries 10 | .map((entry) => { 11 | const [lang, ...slug] = entry.slug.split("/"); 12 | return { params: { lang, slug: slug.join("/") || undefined }, props: entry }; 13 | }) 14 | .filter((entry) => entry.params.lang === "en"); 15 | } 16 | 17 | const entry = Astro.props; 18 | const { Content } = await entry.render(); 19 | 20 | const seo: AstroSeoProps = { 21 | title: `Project ${entry.data.title} - Majestico`, 22 | description: entry.data.description, 23 | canonical: `https://majestico.co/it/work/${entry.slug}/`, 24 | openGraph: { 25 | url: entry.slug, 26 | title: entry.data.title, 27 | description: "{frontmatter.description}", 28 | images: [ 29 | { 30 | url: "https://majestico.co/opengraph/social-og.jpg", 31 | width: 1200, 32 | height: 630, 33 | alt: "Social open graph", 34 | type: "image/jpeg", 35 | }, 36 | { 37 | url: "https://majestico.co/opengraph/twitter-og.jpg", 38 | width: 4096, 39 | height: 4096, 40 | alt: "Twitter open graph", 41 | type: "image/jpeg", 42 | }, 43 | { url: "https://majestico.co/opengraph/social-og.jpg" }, 44 | { url: "https://majestico.co/opengraph/twitter-og.jpg" }, 45 | ], 46 | site_name: "YourSitesName", 47 | }, 48 | twitter: { 49 | handle: "@TwitterHandle", 50 | site: "@site", 51 | cardType: "summary_large_image", 52 | }, 53 | }; 54 | --- 55 | 56 | <MarkdownPostLayout frontmatter={entry} body={entry.body} seo={seo}> 57 | <Content /> 58 | </MarkdownPostLayout> 59 | -------------------------------------------------------------------------------- /src/pages/privacy.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import PrivacyPage from "@/components/infopages/Privacy.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Privacy Policy - Majestico Studio: Your Privacy Matters", 8 | description: 9 | "Our Privacy Policy outlines how Majestico Studio protects your personal information. Discover our commitment to your privacy and data security.", 10 | canonical: "https://majestico.co/privacy/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <PrivacyPage /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/rss.xml.ts: -------------------------------------------------------------------------------- 1 | import rss from "@astrojs/rss"; 2 | import { getCollection } from "astro:content"; 3 | 4 | export async function GET(context: any) { 5 | const blog = await getCollection("posts"); 6 | return rss({ 7 | title: "Majestico's Blog", 8 | description: "A humble Astronaut's guide to the stars", 9 | site: context.site, 10 | items: blog.map((post) => ({ 11 | title: post.data.title, 12 | pubDate: post.data.pubDate, 13 | description: post.data.description, 14 | // Compute RSS link from post `slug` 15 | link: `/blog/${post.slug}/`, 16 | })), 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src/pages/services/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import ServicesMain from "@/components/services/ServicesMain.astro"; 4 | 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | const seo: AstroSeoProps = { 8 | title: "Services | Majestico Studio", 9 | description: "We are a digital studio that creates beautiful and functional websites and applications.", 10 | canonical: "https://majestico.co/services/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <ServicesMain /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/tags/[tag].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | import Tag from "@components/blog/Tag.astro"; 4 | import { getCollection } from "astro:content"; 5 | import { type AstroSeoProps } from "@astrolib/seo"; 6 | 7 | export async function getStaticPaths() { 8 | const allPosts = (await getCollection("posts")).filter((post) => { 9 | const [postLang, ...slug] = post.slug.split("/"); 10 | return postLang === "en"; 11 | }); 12 | 13 | const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())]; 14 | return uniqueTags.map((tag) => { 15 | const filteredPosts = allPosts.filter((post) => post.data.tags.includes(tag)); 16 | return { 17 | params: { tag }, 18 | props: { posts: filteredPosts }, 19 | }; 20 | }); 21 | } 22 | 23 | const { tag } = Astro.params; 24 | const { posts } = Astro.props; 25 | 26 | const seo: AstroSeoProps = { 27 | title: `${tag} | Majestico Studio`, 28 | description: `Explore articles and insights on '${tag}' at Majestico Studio. Dive into a wealth of knowledge covering the latest trends and tips in web design and SEO.`, 29 | canonical: `https://majestico.co/tags/${tag}/`, 30 | }; 31 | --- 32 | 33 | <BaseLayout seo={seo}> 34 | <Tag tag={tag} posts={posts} /> 35 | </BaseLayout> 36 | -------------------------------------------------------------------------------- /src/pages/tags/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | import Tags from "@components/blog/Tags.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Tags - Explore Topics at Majestico Studio", 8 | description: 9 | "Browse through the diverse range of tags at Majestico Studio to find articles and insights tailored to your interests in web design and SEO.", 10 | canonical: "https://majestico.co/tags/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <Tags /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/terms.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import TermsPage from "@/components/infopages/Terms.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Terms of Service - Majestico Studio: Understand Our Terms", 8 | description: 9 | "Read the Terms of Service for Majestico Studio. Learn about our guidelines and your responsibilities when using our web design and SEO services.", 10 | canonical: "https://majestico.co/terms/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <TermsPage /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/thank-you.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@/layouts/BaseLayout.astro"; 3 | import Thanks from "@components/infopages/ThankYou.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | const seo: AstroSeoProps = { 7 | title: "Thank You - Majestico Studio: Email Successfully Sent", 8 | description: 9 | "Thank you for reaching out to Majestico Studio! Your email has been successfully sent. We appreciate your interest and will respond promptly.", 10 | canonical: "https://majestico.co/thank-you/", 11 | }; 12 | --- 13 | 14 | <BaseLayout seo={seo}> 15 | <Thanks /> 16 | </BaseLayout> 17 | -------------------------------------------------------------------------------- /src/pages/work/[...slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import MarkdownPostLayout from "../../layouts/MarkdownPostLayout.astro"; 4 | import { type AstroSeoProps } from "@astrolib/seo"; 5 | 6 | export async function getStaticPaths() { 7 | const projectsEntries = await getCollection("projects"); 8 | 9 | return projectsEntries 10 | .map((entry) => { 11 | const [lang, ...slug] = entry.slug.split("/"); 12 | return { params: { lang, slug: slug.join("/") || undefined }, props: entry }; 13 | }) 14 | .filter((entry) => entry.params.lang === "en"); 15 | } 16 | 17 | const entry = Astro.props; 18 | const { Content } = await entry.render(); 19 | 20 | const seo: AstroSeoProps = { 21 | title: `Project ${entry.data.title} - Majestico`, 22 | description: entry.data.description, 23 | canonical: `https://majestico.co/work/${entry.slug}/`, 24 | openGraph: { 25 | url: entry.slug, 26 | title: entry.data.title, 27 | description: "{frontmatter.description}", 28 | images: [ 29 | { 30 | url: "https://majestico.co/opengraph/social-og.jpg", 31 | width: 1200, 32 | height: 630, 33 | alt: "Social open graph", 34 | type: "image/jpeg", 35 | }, 36 | { 37 | url: "https://majestico.co/opengraph/twitter-og.jpg", 38 | width: 4096, 39 | height: 4096, 40 | alt: "Twitter open graph", 41 | type: "image/jpeg", 42 | }, 43 | { url: "https://majestico.co/opengraph/social-og.jpg" }, 44 | { url: "https://majestico.co/opengraph/twitter-og.jpg" }, 45 | ], 46 | site_name: "YourSitesName", 47 | }, 48 | twitter: { 49 | handle: "@TwitterHandle", 50 | site: "@site", 51 | cardType: "summary_large_image", 52 | }, 53 | }; 54 | --- 55 | 56 | <MarkdownPostLayout frontmatter={entry} body={entry.body} seo={seo}> 57 | <Content /> 58 | </MarkdownPostLayout> 59 | -------------------------------------------------------------------------------- /src/styles/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @font-face { 6 | font-family: "Satoshi"; 7 | src: url("/fonts/Satoshi-Variable.woff2") format("woff"); 8 | font-weight: normal; 9 | font-style: normal; 10 | font-display: swap; 11 | } 12 | 13 | :root { 14 | font-family: "Inter", sans-serif; 15 | } 16 | 17 | @supports (font-variation-settings: normal) { 18 | :root { 19 | font-family: "Inter var", sans-serif; 20 | } 21 | } 22 | 23 | :root { 24 | --grid-rows-color: rgb(255, 255, 255); 25 | --astro-code-color-text: #ffffff; 26 | --astro-code-color-background: #131313; 27 | --astro-code-token-constant: #8b919b; 28 | --astro-code-token-string: #ffffff; 29 | --astro-code-token-comment: #8b919b; 30 | --astro-code-token-keyword: #e83906; 31 | --astro-code-token-parameter: #ffffff; 32 | --astro-code-token-function: #ffffff; 33 | --astro-code-token-string-expression: #c7f801; 34 | --astro-code-token-punctuation: #ffffff; 35 | --astro-code-token-link: #ffffff; 36 | --grid-gap: 1px; 37 | --base-padding-x: max(6vw, 60px); 38 | --base-padding-y: clamp(30px, 4vw, 50px); 39 | } 40 | 41 | @media (max-width: 812px) { 42 | :root { 43 | --base-padding-x: 25px; 44 | --base-padding-y: 25px; 45 | } 46 | } 47 | 48 | html { 49 | scrollbar-gutter: stable; 50 | @apply overflow-x-hidden bg-black text-stone-100; 51 | } 52 | 53 | /* Custom scrollbar */ 54 | 55 | ::-webkit-scrollbar-track { 56 | border: none; 57 | background-color: #676767; 58 | } 59 | ::-webkit-scrollbar { 60 | width: 1px; 61 | background-color: transparent; 62 | } 63 | ::-webkit-scrollbar-thumb { 64 | background-color: #1b1816; 65 | border-radius: 2px; 66 | } 67 | ::-webkit-scrollbar-thumb:hover { 68 | background-color: #000; 69 | } 70 | .dark ::-webkit-scrollbar-thumb { 71 | background-color: #e8e1e1; 72 | } 73 | .dark ::-webkit-scrollbar-thumb:hover { 74 | background-color: #ffffff; 75 | } 76 | 77 | /* Selection */ 78 | 79 | ::-moz-selection { 80 | color: black; 81 | background: white; 82 | } 83 | ::selection { 84 | color: black; 85 | background: white; 86 | } 87 | .dark ::-moz-selection { 88 | color: black; 89 | background: white; 90 | } 91 | .dark ::selection { 92 | color: black; 93 | background: white; 94 | } 95 | 96 | .select-none { 97 | -webkit-tap-highlight-color: transparent; 98 | -webkit-touch-callout: none; 99 | -webkit-user-select: none; 100 | -khtml-user-select: none; 101 | -moz-user-select: none; 102 | -ms-user-select: none; 103 | user-select: none; 104 | } 105 | 106 | .prose-styles { 107 | @apply prose mx-auto max-w-3xl text-lg text-slate-200 prose-headings:font-bold prose-headings:tracking-tighter prose-headings:text-white prose-a:text-slate-300 hover:prose-a:text-white prose-blockquote:border-l-slate-200 prose-blockquote:text-slate-200 prose-code:text-white prose-pre:rounded-lg prose-li:marker:text-slate-300; 108 | } 109 | 110 | .section { 111 | position: relative; 112 | width: 100%; 113 | display: grid; 114 | grid-template-columns: repeat(4, minmax(0, 1fr)); 115 | column-gap: var(--grid-gap); 116 | } 117 | 118 | .dark-section { 119 | color: white; 120 | } 121 | 122 | .squircle { 123 | --squircle-smooth: 1; 124 | --squircle-radius: 10px; 125 | border-radius: var(--squircle-radius); 126 | mask-image: paint(squircle); 127 | } 128 | 129 | .squircle-bg { 130 | --squircle-smooth: 1; 131 | --squircle-radius: 10px; 132 | background: #272521; 133 | mask-image: paint(squircle); 134 | } 135 | 136 | .squircle-bg-white { 137 | --squircle-smooth: 1; 138 | --squircle-radius: 10px; 139 | background: #fff; 140 | mask-image: paint(squircle); 141 | } 142 | 143 | .squircle-hover:hover { 144 | --squircle-smooth: 1; 145 | --squircle-radius: 10px; 146 | background: #272521; 147 | mask-image: paint(squircle); 148 | } 149 | 150 | .squircle-white-hover:hover { 151 | --squircle-smooth: 1; 152 | --squircle-radius: 10px; 153 | background: #fff; 154 | mask-image: paint(squircle); 155 | } 156 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | const defaultTheme = require("tailwindcss/defaultTheme"); 3 | const colors = require("tailwindcss/colors"); 4 | module.exports = { 5 | content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"], 6 | theme: { 7 | screens: { 8 | sm: "540px", 9 | md: "768px", 10 | lg: "1024px", 11 | xl: "1280px", 12 | "2xl": "1536px", 13 | }, 14 | extend: { 15 | borderRadius: { 16 | "4xl": "2rem", 17 | "5xl": "3rem", 18 | "6xl": "4rem", 19 | "7xl": "5rem", 20 | }, 21 | colors: { 22 | primary: { 23 | 50: "#f3ffe4", 24 | 100: "#e4ffc5", 25 | 200: "#c9ff92", 26 | 300: "#a4ff54", 27 | 400: "#82fb20", 28 | 500: "#6cf901", 29 | 600: "#49b400", 30 | 700: "#388902", 31 | 800: "#2f6b09", 32 | 900: "#2a5a0d", 33 | 950: "#113300", 34 | }, 35 | lime: { 36 | 50: "hsl(64, 100%, 95%)", 37 | 100: "hsl(66, 100%, 89%)", 38 | 200: "hsl(68, 100%, 79%)", 39 | 300: "hsl(70, 100%, 66%)", 40 | 400: "hsl(71, 96%, 55%)", 41 | 500: "hsl(72, 99%, 49%)", 42 | 600: "hsl(74, 100%, 35%)", 43 | 700: "hsl(74, 97%, 27%)", 44 | 800: "hsl(75, 84%, 23%)", 45 | 900: "hsl(76, 75%, 20%)", 46 | 950: "hsl(78, 100%, 10%)", 47 | }, 48 | blue: { 49 | 50: "hsl(240, 100%, 97%)", 50 | 100: "hsl(245, 100%, 95%)", 51 | 200: "hsl(244, 100%, 90%)", 52 | 300: "hsl(247, 100%, 83%)", 53 | 400: "hsl(249, 100%, 73%)", 54 | 500: "hsl(252, 100%, 62%)", 55 | 600: "hsl(256, 100%, 54%)", 56 | 700: "hsl(255, 98%, 50%)", 57 | 800: "hsl(255, 97%, 42%)", 58 | 900: "hsl(256, 95%, 37%)", 59 | 950: "hsl(253, 100%, 23%)", 60 | }, 61 | trinidad: { 62 | 50: "hsl(27, 100%, 96%)", 63 | 100: "hsl(28, 100%, 92%)", 64 | 200: "hsl(26, 100%, 83%)", 65 | 300: "hsl(24, 100%, 72%)", 66 | 400: "hsl(20, 100%, 61%)", 67 | 500: "hsl(18, 100%, 53%)", 68 | 600: "hsl(14, 95%, 47%)", 69 | 700: "hsl(11, 93%, 40%)", 70 | 800: "hsl(8, 84%, 34%)", 71 | 900: "hsl(9, 79%, 28%)", 72 | 950: "hsl(7, 86%, 15%)", 73 | }, 74 | }, 75 | fontFamily: { 76 | display: ["Satoshi", ...defaultTheme.fontFamily.sans], 77 | sans: ["Inter", ...defaultTheme.fontFamily.sans], 78 | mono: ["JetBrains Mono", ...defaultTheme.fontFamily.mono], 79 | }, 80 | animation: { 81 | "pulse-fast": "pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite", 82 | }, 83 | }, 84 | }, 85 | safelist: [ 86 | { 87 | pattern: /row-start-\d+/, 88 | variants: ["md"], 89 | }, 90 | { 91 | pattern: /col-start-\d+/, 92 | variants: ["md"], 93 | }, 94 | { 95 | pattern: /translate-y-\d+/, 96 | variants: ["md"], 97 | }, 98 | ], 99 | plugins: [ 100 | require.resolve("prettier-plugin-astro"), 101 | require("@tailwindcss/typography"), 102 | require("@tailwindcss/forms"), 103 | require("@tailwindcss/aspect-ratio"), 104 | ], 105 | overrides: [ 106 | { 107 | files: "*.astro", 108 | options: { 109 | parser: "astro", 110 | }, 111 | }, 112 | ], 113 | }; 114 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "compilerOptions": { 4 | "strictNullChecks": true, 5 | "baseUrl": "src", 6 | "paths": { 7 | "@/*": ["*"], 8 | "@lib/*": ["lib/*"], 9 | "@components/*": ["components/*"], 10 | "@layouts/*": ["layouts/*"], 11 | "@styles/*": ["styles/*"], 12 | "@utils/*": ["utils/*"], 13 | "@pages/*": ["pages/*"], 14 | "@assets/*": ["assets/*"], 15 | "@locales/*": ["locales/*"], 16 | "@i18n/*": ["i18n/*"], 17 | }, 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /unlighthouse.config.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="unlighthouse" /> 2 | import { defineConfig } from "unlighthouse"; 3 | 4 | /* 5 | * https://unlighthouse.dev/api/config 6 | */ 7 | export default defineConfig({ 8 | site: "majestico.co", 9 | scanner: { 10 | exclude: [/^\/cdn-cgi\//], 11 | }, 12 | ci: { 13 | budget: 90, 14 | buildStatic: true, 15 | }, 16 | debug: true, 17 | cache: false, 18 | }); 19 | --------------------------------------------------------------------------------