From 8ac090aff38175745a4b33845a8bddeeb11ff81e Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Fri, 6 Feb 2026 00:20:55 +0100 Subject: [PATCH] feat: Set up new Tailwind CSS configuration, global styling, and initial application pages. --- packages/gatekeeper/dev-output.log | 15 ++ packages/gatekeeper/package.json | 1 + packages/gatekeeper/postcss.config.cjs | 6 + packages/gatekeeper/public/icon-white.svg | 12 ++ .../src/app/gatekeeper/login/page.tsx | 157 +++++++++--------- packages/gatekeeper/src/app/globals.css | 87 ++++++++-- packages/gatekeeper/src/app/layout.tsx | 11 +- packages/gatekeeper/src/app/page.tsx | 5 + packages/gatekeeper/tailwind.config.cjs | 59 +++++++ packages/gatekeeper/tailwind.config.js | 23 --- pnpm-lock.yaml | 24 +++ 11 files changed, 286 insertions(+), 114 deletions(-) create mode 100644 packages/gatekeeper/dev-output.log create mode 100644 packages/gatekeeper/postcss.config.cjs create mode 100644 packages/gatekeeper/public/icon-white.svg create mode 100644 packages/gatekeeper/src/app/page.tsx create mode 100644 packages/gatekeeper/tailwind.config.cjs delete mode 100644 packages/gatekeeper/tailwind.config.js diff --git a/packages/gatekeeper/dev-output.log b/packages/gatekeeper/dev-output.log new file mode 100644 index 0000000..fb581de --- /dev/null +++ b/packages/gatekeeper/dev-output.log @@ -0,0 +1,15 @@ + +> @mintel/gatekeeper@1.0.0 dev +> next dev + + ⚠ Port 3000 is in use, trying 3001 instead. + ▲ Next.js 15.1.6 + - Local: http://localhost:3001 + - Network: http://192.168.1.126:3001 + - Experiments (use with caution): + · clientTraceMetadata + + ✓ Starting... +warn - It seems like you don't have a global error handler set up. It is recommended that you add a global-error.js file with Sentry instrumentation so that React rendering errors are reported to Sentry. Read more: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#react-render-errors-in-app-router (you can suppress this warning by setting SENTRY_SUPPRESS_GLOBAL_ERROR_HANDLER_FILE_WARNING=1 as environment variable) + ✓ Ready in 2.7s +[?25h diff --git a/packages/gatekeeper/package.json b/packages/gatekeeper/package.json index d839fc3..da4c46e 100644 --- a/packages/gatekeeper/package.json +++ b/packages/gatekeeper/package.json @@ -24,6 +24,7 @@ "@mintel/eslint-config": "workspace:*", "@mintel/next-config": "workspace:*", "@mintel/tsconfig": "workspace:*", + "@tailwindcss/typography": "^0.5.19", "@types/node": "^20.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", diff --git a/packages/gatekeeper/postcss.config.cjs b/packages/gatekeeper/postcss.config.cjs new file mode 100644 index 0000000..67cdf1a --- /dev/null +++ b/packages/gatekeeper/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/gatekeeper/public/icon-white.svg b/packages/gatekeeper/public/icon-white.svg new file mode 100644 index 0000000..d285a05 --- /dev/null +++ b/packages/gatekeeper/public/icon-white.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/gatekeeper/src/app/gatekeeper/login/page.tsx b/packages/gatekeeper/src/app/gatekeeper/login/page.tsx index c0b0bd4..f050e76 100644 --- a/packages/gatekeeper/src/app/gatekeeper/login/page.tsx +++ b/packages/gatekeeper/src/app/gatekeeper/login/page.tsx @@ -1,6 +1,7 @@ import { cookies } from "next/headers"; import { redirect } from "next/navigation"; -import { Lock, ShieldCheck, ArrowRight } from "lucide-react"; +import { ArrowRight, ShieldCheck } from "lucide-react"; +import Image from "next/image"; interface LoginPageProps { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; @@ -12,7 +13,6 @@ export default async function LoginPage({ searchParams }: LoginPageProps) { const error = params.error === "1"; const projectName = process.env.PROJECT_NAME || "Mintel"; - const projectColor = process.env.PROJECT_COLOR || "#82ed20"; async function login(formData: FormData) { "use server"; @@ -41,92 +41,93 @@ export default async function LoginPage({ searchParams }: LoginPageProps) { } return ( -
- {/* Background Decor */} -
+
+ {/* Background Decor - Signature mintel.me style */}
-
- {/* Logo / Icon */} -
-
- {projectName} -
-
- -
- {/* Subtle accent line */} -
- -
-

- {projectName.split(" ")[0]} - GATEKEEPER -

-

- Restricted Infrastructure Access -

-
- - {error && ( -
- - Invalid access password. Please try again. -
- )} - -
- - -
- - +
+ {/* Top Icon Box - Signature mintel.me Black Square */} +
+
+ Mintel
+
- - -
+
+
+

+ {projectName} Gatekeeper +

+

+ + Infrastructure Protection + +

+
-
-

- © 2026 {projectName} Infrastructure -

+ {error && ( +
+ + Access Denied. Try Again. +
+ )} + +
+ + +
+ +
+ + +
+ + {/* Bottom Section - Full Branding Parity */} +
+
+
+ {projectName} +
+

+ © 2026 MINTEL +

+
+
-
+
); } diff --git a/packages/gatekeeper/src/app/globals.css b/packages/gatekeeper/src/app/globals.css index 28c9379..186933e 100644 --- a/packages/gatekeeper/src/app/globals.css +++ b/packages/gatekeeper/src/app/globals.css @@ -2,20 +2,83 @@ @tailwind components; @tailwind utilities; -:root { - --background: #000c1f; - --foreground: #ffffff; +@layer base { + html { + scroll-behavior: smooth; + } + + body { + @apply bg-white text-slate-800 font-serif antialiased selection:bg-slate-900 selection:text-white; + line-height: 1.6; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + @apply font-sans font-bold text-slate-900 tracking-tighter; + } + + p { + @apply mb-4 text-base leading-relaxed text-slate-700; + } + + a { + @apply text-slate-900 hover:text-slate-700 transition-colors no-underline; + } } -body { - color: var(--foreground); - background: var(--background); - min-height: 100vh; +@layer components { + .narrow-container { + @apply max-w-4xl mx-auto px-6 py-10; + } + + .btn { + @apply inline-flex items-center justify-center px-6 py-3 border border-slate-200 bg-white text-slate-600 font-sans font-bold text-sm uppercase tracking-widest rounded-full transition-all duration-500 ease-industrial hover:border-slate-400 hover:text-slate-900 hover:bg-slate-50 hover:-translate-y-0.5 hover:shadow-xl hover:shadow-slate-100 active:translate-y-0 active:shadow-sm; + } + + .btn-primary { + @apply border-slate-900 text-slate-900 hover:bg-slate-900 hover:text-white; + } } -.bg-grid { - background-image: - linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px), - linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px); - background-size: 40px 40px; +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: #f1f5f9; +} + +::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +/* Animations */ +@keyframes shake { + 0%, + 100% { + transform: translateX(0); + } + + 25% { + transform: translateX(-4px); + } + + 75% { + transform: translateX(4px); + } +} + +.animate-shake { + animation: shake 0.2s ease-in-out 0s 2; } diff --git a/packages/gatekeeper/src/app/layout.tsx b/packages/gatekeeper/src/app/layout.tsx index 7d6830f..c466d6c 100644 --- a/packages/gatekeeper/src/app/layout.tsx +++ b/packages/gatekeeper/src/app/layout.tsx @@ -1,6 +1,15 @@ import type { Metadata } from "next"; +import { Inter, Newsreader } from "next/font/google"; import "./globals.css"; +const inter = Inter({ subsets: ["latin"], variable: "--font-inter" }); +const newsreader = Newsreader({ + subsets: ["latin"], + variable: "--font-newsreader", + style: "italic", + display: "swap", +}); + export const metadata: Metadata = { title: "Gatekeeper | Access Control", description: "Mintel Infrastructure Protection", @@ -12,7 +21,7 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - + {children} ); diff --git a/packages/gatekeeper/src/app/page.tsx b/packages/gatekeeper/src/app/page.tsx new file mode 100644 index 0000000..ada07bb --- /dev/null +++ b/packages/gatekeeper/src/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function RootPage() { + redirect("/gatekeeper/login"); +} diff --git a/packages/gatekeeper/tailwind.config.cjs b/packages/gatekeeper/tailwind.config.cjs new file mode 100644 index 0000000..c8bf643 --- /dev/null +++ b/packages/gatekeeper/tailwind.config.cjs @@ -0,0 +1,59 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + borderRadius: { + xl: "1rem", + "2xl": "1.5rem", + "3xl": "2rem", + full: "9999px", + }, + colors: { + slate: { + 850: "#1e293b", + 900: "#0f172a", + 950: "#020617", + }, + }, + fontFamily: { + sans: ["var(--font-inter)", "Inter", "system-ui", "sans-serif"], + serif: ["var(--font-newsreader)", "Georgia", "serif"], + mono: ["JetBrains Mono", "monospace"], + }, + animation: { + "fade-in": "fadeIn 0.5s ease-in-out", + "slide-up": "slideUp 0.6s ease-out", + "slide-down": "slideDown 0.6s ease-out", + shake: "shake 0.2s ease-in-out 0s 2", + }, + keyframes: { + fadeIn: { + "0%": { opacity: "0" }, + "100%": { opacity: "1" }, + }, + slideUp: { + "0%": { transform: "translateY(20px)", opacity: "0" }, + "100%": { transform: "translateY(0)", opacity: "1" }, + }, + slideDown: { + "0%": { transform: "translateY(-20px)", opacity: "0" }, + "100%": { transform: "translateY(0)", opacity: "1" }, + }, + shake: { + "0%, 100%": { transform: "translateX(0)" }, + "25%": { transform: "translateX(-4px)" }, + "75%": { transform: "translateX(4px)" }, + }, + }, + transitionTimingFunction: { + industrial: "cubic-bezier(0.23, 1, 0.32, 1)", + }, + }, + }, + plugins: [require("@tailwindcss/typography")], +}; diff --git a/packages/gatekeeper/tailwind.config.js b/packages/gatekeeper/tailwind.config.js deleted file mode 100644 index 51bb651..0000000 --- a/packages/gatekeeper/tailwind.config.js +++ /dev/null @@ -1,23 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", - "./src/components/**/*.{js,ts,jsx,tsx,mdx}", - "./src/app/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: { - colors: { - mintel: { - green: "#82ed20", - blue: "#001a4d", - dark: "#000c1f", - }, - }, - backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - }, - }, - }, - plugins: [], -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4411bf5..62bb93c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,6 +200,9 @@ importers: '@mintel/tsconfig': specifier: workspace:* version: link:../tsconfig + '@tailwindcss/typography': + specifier: ^0.5.19 + version: 0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2)) '@types/node': specifier: ^20.0.0 version: 20.19.30 @@ -1832,6 +1835,11 @@ packages: '@swc/types@0.1.25': resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} @@ -3138,6 +3146,7 @@ packages: glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} @@ -3741,6 +3750,7 @@ packages: next@15.1.6: resolution: {integrity: sha512-Hch4wzbaX0vKQtalpXvUiw5sYivBy4cm5rzUKrBnUB/y436LGrvOUqYvlSeNVCWFO/770gDlltR9gqZH62ct4Q==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/CVE-2025-66478 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -3991,6 +4001,10 @@ packages: peerDependencies: postcss: ^8.2.14 + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} @@ -6498,6 +6512,11 @@ snapshots: dependencies: '@swc/counter': 0.1.3 + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.2) + '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.28.6 @@ -8890,6 +8909,11 @@ snapshots: postcss: 8.5.6 postcss-selector-parser: 6.1.2 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0