+
+
- const isPrimary = group.id === 'primary';
-
- return (
-
- :
- group.id === 'entertainment' ? :
- group.id === 'rm8pfix-vn' ? :
-
- }
- >
- {groupLinks.map((link, linkIndex) => (
-
- ))}
-
-
- {/* Position Search Bar immediately after the Primary Featured section */}
- {isPrimary && (
- }
-
- );
- })}
-
- {searchQuery && filteredLinks.length === 0 && (
-
+ {sortedLayout.map((piece, idx) => (
+
+ ))}
-
);
diff --git a/src/components/FeaturedCard.jsx b/src/components/FeaturedCard.jsx
new file mode 100644
index 0000000..f142e44
--- /dev/null
+++ b/src/components/FeaturedCard.jsx
@@ -0,0 +1,86 @@
+import React from 'react';
+import { motion } from 'framer-motion';
+
+const FeaturedCard = ({ title, subtitle, url, type, videoSrc, index = 0 }) => {
+ return (
+
+ Khoa.vo
+ where design meets intelligence
+
+
+
+
+
+
-
-
- )}
-
- {groupIndex < groups.length - 1 && !isPrimary &&
-
-
- SEARCH
-
-
-
- setSearchQuery(e.target.value)}
- />
-
-
-
- )}
+ [ ERROR: NO ACCESS MATCHED ]
-
+
+
+
+ featured
+
+
+
+
+ {type === 'cv' && videoSrc && (
+
+
+
+ )}
+
+ + {title} +
+ ++ {subtitle} +
+ +
+
+ enter
+
+
+
-
-
-
- {/* Manual Divider Pattern */}
-
+
);
};
-export default Header;
+export default Header;
\ No newline at end of file
diff --git a/src/components/PortalCard.jsx b/src/components/PortalCard.jsx
index c538997..7eb968a 100644
--- a/src/components/PortalCard.jsx
+++ b/src/components/PortalCard.jsx
@@ -1,47 +1,19 @@
import React from 'react';
-import { motion } from 'framer-motion';
const PortalCard = ({ item, index }) => {
- const { title, subtitle, url, color } = item;
+ const { title, subtitle, url } = item;
return (
-
-
+ {title}
+ {subtitle && {subtitle}}
+
);
};
-export default PortalCard;
+export default PortalCard;
\ No newline at end of file
diff --git a/src/components/Section.jsx b/src/components/Section.jsx
index 7ce7ac2..f08dfec 100644
--- a/src/components/Section.jsx
+++ b/src/components/Section.jsx
@@ -1,67 +1,41 @@
-import React, { useRef } from 'react';
-import { motion, useInView } from 'framer-motion';
-
-const Section = ({ title, description, children, index, isFeatured, featuredImage }) => {
- const ref = useRef(null);
- const isInView = useInView(ref, { once: true, margin: "-100px" });
-
- // Format index as 001, 002, etc.
- const figNumber = String(index + 1).padStart(3, '0');
- const isEven = index % 2 === 0;
+import React from 'react';
+import { motion } from 'framer-motion';
+const Section = ({ title, children }) => {
return (
-
-
- VNDANGKHOA
-
-
-
- VNDANGKHOA
PORTAL
-
-
-
- - A reference manual for people who design and build software. -
-- Managed and illustrated by Khoa Vo. -
-
-
-
- {/* Technical bracket decorations */}
-
-
- [ PORT_ACCESS ]
-
-
-
-
- - → {title.toUpperCase()} -
- -- {subtitle} -
-
-
-
- EST. 2026
-
-
-
- {/* Sidebar Label - Desktop Only */}
-
- FIG. {figNumber}
-
-
- {/* Content Column (Text & Buttons) */}
-
-
-
- {/* Blueprint Column (Animation) */}
- - {title} -
- -
- {description}
-
-
-
- {children}
-
-
- {featuredImage && (
-
- {typeof featuredImage === 'string' ? (
-
- ) : (
-
+
);
};
diff --git a/src/data/links.js b/src/data/links.js
index 172800d..de5eccd 100644
--- a/src/data/links.js
+++ b/src/data/links.js
@@ -22,37 +22,28 @@ export const links = [
{
id: 3,
title: "YouTube",
- subtitle: "No Ads",
+ subtitle: "Ad-free",
url: "https://ut.khoavo.myds.me",
icon: Youtube,
- group: "entertainment",
+ group: "media",
order: 1
},
{
id: 4,
title: "TikTok",
- subtitle: "No Ads",
+ subtitle: "Ad-free",
url: "https://tt.khoavo.myds.me",
icon: Video,
- group: "entertainment",
+ group: "media",
order: 2
},
{
id: 5,
- title: "Music",
- subtitle: "Streaming",
- url: "https://music.khoavo.myds.me",
- icon: Music,
- group: "entertainment",
- order: 3
- },
- {
- id: 6,
title: "Spotify",
subtitle: "Streaming",
url: "https://sp.khoavo.myds.me",
icon: Music,
- group: "entertainment",
+ group: "media",
order: 4
},
{
@@ -61,16 +52,16 @@ export const links = [
subtitle: "Streaming",
url: "https://nf.khoavo.myds.me",
icon: Film,
- group: "entertainment",
+ group: "media",
order: 5
},
{
id: 8,
title: "RM8PFix",
- subtitle: "Fixes & Tools",
+ subtitle: "Tools",
url: "https://rm8pfix.khoavo.myds.me",
icon: Wrench,
- group: "rm8pfix-vn",
+ group: "tools",
order: 1
},
{
@@ -79,7 +70,7 @@ export const links = [
subtitle: "Save",
url: "https://save.khoavo.myds.me",
icon: HardDrive,
- group: "rm8pfix-vn",
+ group: "tools",
order: 2
},
{
@@ -88,7 +79,7 @@ export const links = [
subtitle: "Free",
url: "https://free.khoavo.myds.me",
icon: Wrench,
- group: "rm8pfix-vn",
+ group: "tools",
order: 3
},
{
@@ -97,8 +88,8 @@ export const links = [
subtitle: "Tools",
url: "https://pdf.khoavo.myds.me",
icon: FileText,
- group: "dev",
- order: 1
+ group: "tools",
+ order: 4
},
{
id: 12,
@@ -106,43 +97,55 @@ export const links = [
subtitle: "Tools",
url: "https://jpg.khoavo.myds.me",
icon: Image,
- group: "dev",
- order: 2
+ group: "tools",
+ order: 5
+ },
+{
+ id: 6,
+ title: "Netflix",
},
{
- id: 13,
+ id: 7,
+ title: "RM8PFix",
+ },
+ {
+ id: 8,
+ title: "Portal",
+ },
+ {
+ id: 9,
+ title: "Free",
+ },
+ {
+ id: 10,
+ title: "PDF",
+ },
+ {
+ id: 11,
+ title: "JPG",
+ },
+ {
+ id: 12,
title: "IT Utilities",
subtitle: "Dev Tools",
url: "https://it.khoavo.myds.me",
icon: Terminal,
- group: "dev",
- order: 3
+ group: "tools",
+ order: 6
},
];
export const groups = [
{
id: 'primary',
- title: 'PRIMARY',
- icon: Briefcase,
- description: "Have you ever wondered how your digital presence is routed across different platforms? The primary resource layer acts as the centralized directory for all professional identities. These components are hard-coded for maximum reliability and persistent access to the core VNDK ecosystem."
+ title: 'primary',
},
{
- id: 'entertainment',
- title: 'MEDIA_DISTRIBUTION',
- icon: Monitor,
- description: "Digital consumption and leisure systems operate on a separate high-fidelity distribution plane. By utilizing unauthorized ad-blocking layers and clean streaming protocols, we ensure that media remains an uninterrupted experience within the portal."
+ id: 'media',
+ title: 'media',
},
{
- id: 'rm8pfix-vn',
- title: 'SYSTEM_MAINTENANCE',
- icon: Wrench,
- description: "This terminal routes directly to the REDMAGIC hardware modification group. Representing flagship architecture (builds 8, 9, and 10 Pro), these devices feature top-tier Snapdragon processing and kernel-level flexibility, granting enthusiasts absolute administrative control to deeply customize their systems."
- },
- {
- id: 'dev',
- title: 'DEVELOPMENT_STACK',
- icon: Cpu,
- description: "Because our software tools are built on primitive data types, we need a robust stack to handle rasterization, PDF serialization, and IT utility execution. This toolkit provides the necessary abstractions to manipulate system state at the binary level."
+ id: 'tools',
+ title: 'tools',
},
];
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index 57ea771..19f160a 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,113 +1,184 @@
-@import url('https://fonts.googleapis.com/css2?family=Silkscreen:wght@400;700&family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&family=IBM+Plex+Mono:wght@300;400;500;600;700&display=swap');
-
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
+@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&display=swap');
:root {
- --bg-primary: #ffffff;
+ --font-mono: 'IBM Plex Mono', monospace;
+ --cell-size: 9vw;
+
+ /* Light theme (default) */
+ --bg-cream: #ffffff;
+ --bg-board: #ffffff;
--text-primary: #000000;
- --text-secondary: #6b7280;
- --accent: #3147ba;
- --accent-soft: rgba(49, 71, 186, 0.05);
- --border-color: #e5e7eb;
+ --text-secondary: #666666;
+ --border-color: #000000;
+ --cell-default: #c0c0c0;
}
-@layer base {
- body {
- @apply bg-white text-black antialiased font-serif;
- background-image:
- linear-gradient(var(--accent-soft) 1px, transparent 1px),
- linear-gradient(90deg, var(--accent-soft) 1px, transparent 1px);
- background-size: 24px 24px;
- }
+[data-theme="dark"] {
+ --bg-cream: #1a1a1a;
+ --bg-board: #222222;
+ --text-primary: #ffffff;
+ --text-secondary: #888888;
+ --border-color: #444444;
+ --cell-default: #333333;
+}
- h1, h2, h3, .font-pixel {
- font-family: 'Silkscreen', cursive;
- }
+* { margin: 0; padding: 0; box-sizing: border-box; }
- p, .font-serif {
- font-family: 'PT Serif', serif;
- }
+html, body {
+ width: 100%;
+ overflow-x: hidden;
+}
- code, pre, .font-mono {
- font-family: 'IBM Plex Mono', monospace;
+body {
+ font-family: var(--font-mono);
+ background: var(--bg-cream);
+}
+
+a { color: inherit; text-decoration: none; }
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 20px;
+ background: var(--bg-cream);
+ border-bottom: 2px solid var(--border-color);
+}
+
+.header-name { font-size: 14px; font-weight: 500; }
+.header-nav a { font-size: 10px; color: var(--text-secondary); margin-left: 16px; }
+
+.theme-toggle {
+ background: none;
+ border: 1px solid var(--border-color);
+ padding: 4px 8px;
+ font-size: 10px;
+ font-family: inherit;
+ cursor: pointer;
+}
+
+.tetris-board {
+ display: grid;
+ grid-template-columns: repeat(10, 1fr);
+ grid-template-rows: repeat(8, 1fr);
+ align-content: end;
+ gap: 0;
+ width: 100%;
+ max-width: 500px;
+ margin: 10px auto;
+ padding: 0 10px;
+ background: var(--bg-board);
+ contain: layout;
+}
+
+@media (max-width: 400px) {
+ .tetris-board {
+ gap: 0;
+ padding: 0 4px;
+ }
+ .tetris-piece {
+ border: none !important;
+ }
+ .header-tagline {
+ display: none;
}
}
-/* Technical Layout Utilities */
-.ms-container {
- @apply max-w-[1100px] mx-auto px-6 md:px-12;
+@media (max-width: 500px) {
+ .header-tagline {
+ display: none;
+ }
}
-/* Dropped Cap */
-.drop-cap::first-letter {
- @apply text-6xl font-bold float-left mr-3 leading-[0.8] mt-2;
- font-family: 'PT Serif', serif;
+@media (max-width: 500px) {
+ .footer {
+ width: 100% !important;
+ max-width: 100%;
+ padding: 8px 16px;
+ }
}
-/* Wavy Highlight (Refined for Pixel Perfect) */
-.ms-highlight {
- @apply relative inline-block;
- text-decoration: underline wavy var(--accent);
- text-underline-offset: 4px;
+html, body {
+ width: 100%;
+ overflow-x: hidden;
+ height: 100%;
}
-/* Dot Grid Divider */
-.dot-divider {
- @apply w-full h-8 mb-4;
- background-image: radial-gradient(circle, #e5e7eb 1.5px, transparent 1.5px);
- background-size: 16px 16px;
+body {
+ font-family: var(--font-mono);
+ background: var(--bg-cream);
}
-/* Blueprint Image Container */
-.blueprint-container {
- @apply relative border border-gray-100 bg-white p-2 shadow-sm transition-all duration-500;
- background-image:
- linear-gradient(var(--accent-soft) 1px, transparent 1px),
- linear-gradient(90deg, var(--accent-soft) 1px, transparent 1px);
- background-size: 16px 16px;
+.tetris-piece {
+ display: block;
+ width: 100%;
+ height: 100%;
+ transition: background 0.15s ease;
+ opacity: 0;
+ animation: dropIn 0.5s ease-out forwards;
+ animation-fill-mode: forwards;
+ border: none !important;
+ outline: none !important;
+ box-shadow: none !important;
+ -webkit-border-radius: 0;
+ border-radius: 0;
}
-.blueprint-container img {
- @apply mix-blend-multiply opacity-90;
+.tetris-piece * {
+ border: none !important;
+ outline: none !important;
+ box-shadow: none !important;
}
-.manual-text {
- @apply text-[17px] md:text-[18px] leading-[1.6] text-gray-900;
+.tetris-piece.show {
+ animation: dropIn 0.5s ease-out forwards;
+ animation-fill-mode: forwards;
}
-/* Card Styling (Technical Labels) */
-.tech-label {
- @apply border-2 border-gray-100 bg-white p-4 md:p-5 transition-all duration-300;
- box-shadow: 2px 2px 0px rgba(0, 0, 0, 0.05);
+@keyframes colorBlink {
+ 0%, 100% {
+ filter: brightness(1);
+ }
+ 50% {
+ filter: brightness(1.25) saturate(1.1);
+ }
}
-.tech-label:hover {
- @apply border-blue-600 shadow-blue-100 bg-blue-50/10;
- box-shadow: 6px 6px 0px var(--accent-soft);
- transform: translate(-3px, -3px);
+@media (max-width: 500px) {
+ @keyframes colorBlink {
+ 0%, 100% {
+ filter: brightness(1);
+ }
+ 50% {
+ filter: brightness(1.08) saturate(1.02);
+ }
+ }
}
-/* Vertical Text Helper */
-.vertical-text {
- writing-mode: vertical-rl;
+@keyframes dropIn {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
}
-/* Custom Scrollbar */
-::-webkit-scrollbar {
- width: 10px;
+@keyframes colorBlink {
+ 0%, 100% {
+ filter: brightness(1);
+ }
+ 50% {
+ filter: brightness(1.3);
+ }
}
-::-webkit-scrollbar-track {
- background: white;
-}
-
-::-webkit-scrollbar-thumb {
- background: var(--accent-soft);
- border: 4px solid white;
-}
-
-::-webkit-scrollbar-thumb:hover {
- background: var(--accent);
+.footer {
+ padding: 10px 20px;
+ font-size: 10px;
+ color: var(--text-secondary);
+ width: calc(10 * var(--cell-size));
+ margin: 0 auto;
+ text-align: center;
+ margin-top: auto;
}
\ No newline at end of file
diff --git a/tailwind.config.js b/tailwind.config.js
index ce81402..89a305e 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,28 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
- darkMode: 'class',
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
- extend: {
- fontFamily: {
- sans: ['Inter', 'sans-serif'],
- },
- animation: {
- 'blob': 'blob 7s infinite',
- },
- keyframes: {
- blob: {
- '0%': { transform: 'translate(0px, 0px) scale(1)' },
- '33%': { transform: 'translate(30px, -50px) scale(1.1)' },
- '66%': { transform: 'translate(-20px, 20px) scale(0.9)' },
- '100%': { transform: 'translate(0px, 0px) scale(1)' },
- }
- }
- },
+ extend: {},
},
plugins: [],
-}
-
+}
\ No newline at end of file
diff --git a/vite.config.js b/vite.config.js
index d9ffa39..d660962 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,7 +4,14 @@ import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
- base: './',
+ base: '/',
+ server: {
+ host: '127.0.0.1',
+ port: 5173,
+ },
+ preview: {
+ port: 4173,
+ },
build: {
sourcemap: false,
},
- {featuredImage}
-
- )}
-
- SYSTEM_SCHEMATIC_V.1
- ID_{figNumber}
+
+
+
-
+ {title && (
+
+ )}
+
+
+
-
- )}
- + {title} +
+
+ {children}
+
+