RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/bryaanf-liantportfolio-clinerules-project-guidelines ↔ bryaanf-liantportfolio-clinerules-ui-standards

Comparison

A · Cline rules · BryaanF/LiantPortfolioB · Cline rules · BryaanF/LiantPortfolio
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections014420%
Commands0300%
Section tags42440%

What each file covers

Sections

0 shared · 14 only in A · 42 only in B
  • − Liant Portfolio - Project Guidelines
  • − Brief overview
  • − Site language policy
  • − Communication style
  • − Styling conventions
  • − Input and form handling
  • − State management
  • − Commit message conventions
  • − Library and dependency policy
  • − Section visibility control
  • − Build and deployment notes
  • − Reusable component structure
  • − Accessibility baseline
  • − Internationalization note
  • + Liant Portfolio - UI Standards & Component Patterns
  • + 1. COLOR SYSTEM
  • + 1.1 Brand Gold (Primary Accent)
  • + 1.2 Theme Tokens (CSS Custom Properties)
  • + 1.3 Tailwind Arbitrary Value Pattern
  • + 2. SECTION LAYOUT PATTERN
  • + Section IDs (used by header nav anchors)
  • + 3. ANIMATION CONVENTIONS
  • + 3.1 Scroll-Triggered Reveal (Framer Motion)
  • + 3.2 Hover Animations
  • + 3.3 Floating/Looping Animation (for decorative elements)
  • + 4. TYPOGRAPHY CONVENTIONS
  • + 5. SECTION HEADER PATTERN (STANDARDIZED)
  • + Usage
  • + Component API
  • + Rendered Output
  • + Example Usage by Section
  • + 6. CARD STANDARDS
  • + 6.1 Card Container
  • + 6.2 Card Hover Border Accent
  • + 6.3 Bullet/List Items
  • + 6.4 Spec/Info Grid (2-column metadata)
  • + 7. BUTTON PATTERNS
  • + 7.1 Primary Button (Gold fill)
  • + 7.2 Secondary/Outline Button
  • + 7.3 Danger/Remove Button
  • + 7.4 Legacy `.main-button` (used by Button.jsx component)
  • + 8. SVG ICON PATTERN
  • + 8.1 Inline SVGs (Recommended for new code)
  • + 8.2 FontAwesome (Used in SocialMedia, Contact, header)
  • + 8.3 React Icons (Installed but unused — avoid if possible)
  • + 9. FORM BLUEPRINT (WhatsApp/Email Actions)
  • + 10. IMAGE HANDLING (Vite)
  • + 11. LANGUAGE / I18N PATTERN
  • + 12. MODAL / OVERLAY PATTERNS
  • + 12.1 ImageLightbox (`src/components/imageLightbox/ImageLightbox.jsx`)
  • + 12.2 ProjectShowcase (`src/components/projectShowcase/ProjectShowcase.jsx`)
  • + 13. THEME TOGGLE SYSTEM
  • + 14. LOTTIE ANIMATION PATTERN
  • + 15. FRAMER MOTION IMPORTS
  • + 16. KNOWN INCONSISTENCIES & MIGRATION NOTES
  • + 17. FILE ORGANIZATION SUMMARY

Commands

0 shared · 3 only in A · 0 only in B
  • − gh-pages
  • − npm run start
  • − npm run build

Section tags

4 shared · 2 only in A · 4 only in B
  • − build
  • − git-pr
  • + test
  • + lint-format
  • + database
  • + api
  •   code-style
  •   architecture
  •   ui
  •   do-not

Line diff

+557 added−89 removed35 unchanged5.9% identical
BryaanF/LiantPortfolio · .clinerules/project-guidelines.md
@@ −1 @@
1# Liant Portfolio - Project Guidelines
2 
3> **Compatibility Note:** This guideline file is designed to be tool-agnostic and readable by any AI coding assistant (Cline, Copilot, future tools). It supplements the existing [.github/copilot-instructions.md](../.github/copilot-instructions.md) with additional conventions and preferences not covered there.
4 
5## Brief overview
6 
7- **Project:** A data-driven, single-page portfolio website for Brilliant Fikri (Liant), built with React 18 + Vite + Tailwind CSS + SCSS.
8- **Nature:** Pure frontend — no backend, no database. All content lives in `src/portfolio.jsx` as a single configuration file.
9- **Deployment:** Static site deployed to GitHub Pages (`gh-pages` branch) via GitHub Actions from `master` branch.
10- **Domain:** [brilliantfikri.com](https://brilliantfikri.com)
11 
12## Site language policy
13 
14- **Primary language for all user-facing content is English.** The portfolio targets an international/professional audience.
15- **Pending revision:** `pricingSection` in `src/portfolio.jsx` is currently in Bahasa Indonesia and must be translated to English. This includes all `title`, `description`, `desc`, `longDesc`, `features`, `notIncluded`, `workflow` fields, and the WhatsApp checkout message template in `src/containers/pricing/Pricing.jsx` (`handleCheckout` function).
16- Comments in code may use either English or Bahasa Indonesia — both are acceptable.
17- Communication with the developer (AI or human) may use Bahasa Indonesia.
 
18 
19## Communication style
20 
21- **Concise and direct.** Avoid filler words and conversational fluff in code comments, commit messages, and documentation.
22- Code comments should explain the "why," not the "what" — the code itself should be self-documenting for what it does.
23- **SCSS comments** use `/* */` block style for section headers (see existing pattern in `variables.scss`).
24- **JSX comments:** `//` for inline, `/* */` for multi-line explanations.
25 
26## Styling conventions
27 
28- **Tailwind CSS is the primary styling approach** for all new components and sections. Use utility classes directly in JSX (reference `src/containers/pricing/Pricing.jsx` as the canonical example).
29- **SCSS is reserved for reusable custom components** that need encapsulated, complex styles not easily expressed with Tailwind utilities. When creating a new reusable component in `src/components/`, co-locate a `.scss` file in the same folder.
30- **Design tokens:** Always reference CSS custom properties for theme-switchable values:
31 - `var(--bg-body)`, `var(--bg-card)`, `var(--text-primary)`, `var(--text-secondary)`, `var(--btn-primary-bg)`, `var(--border-light)`
32 - These are defined in `src/variables.scss` for both `.light-mode` and `.dark-mode`.
33 - For Tailwind, use arbitrary value syntax when needed: `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`, `border-[var(--border-light)]`.
34- **SCSS variables** (prefixed with `$`) are for non-theme values only (e.g., `$brand-gold`, `$buttonHover`, static colors). Do not use SCSS variables for colors that should respond to dark/light mode.
35- **Never hardcode colors** in component JSX or SCSS that should respect the theme. Always go through CSS custom properties.
36- **Brand color:** Gold `#a1902e` (`$brand-gold`). This is the primary accent across buttons, links, and highlights.
37- **IMPORTANT:** The brand-gold in `tailwind.config.js` must match `$brand-gold` in `variables.scss`. Currently both use `#a1902e`. If one changes, update the other.
38 
39## Input and form handling
 
 
 
 
 
 
 
 
40 
41- **This project has no backend.** There is no server to POST form data to.
42- All "contact" or "checkout" interactions must use **direct action links:**
43 - **WhatsApp:** `https://wa.me/6281331487753?text=...` with a pre-formatted, URL-encoded message.
44 - **Email:** `mailto:briliantfikri@gmail.com` with optional `?subject=...&body=...` parameters.
45- Do NOT create `<form>` elements with submit handlers — there is nothing to handle submissions.
46- Do NOT add any form validation libraries or backend integration code.
47 
48## State management
 
 
 
 
 
 
 
 
 
 
49 
50- **Context API** is the established pattern for global state (see `src/contexts/StyleContext.js` for theme).
51- **Local state** (`useState` / `useReducer`) is sufficient for component-specific state.
52- **Custom hooks** in `src/hooks/` for reusable state logic (e.g., `useLocalStorage` for persistence).
53- Do NOT introduce Redux, Zustand, or other external state management libraries — they are unnecessary for this project's complexity level.
54 
55## Commit message conventions
56 
57Use structured, prefixed commit messages. The format is: `<type>: <brief description>`
58 
59| Prefix | Use when |
60| ----------- | ---------------------------------------------------------------- |
61| `feat:` | Adding a new section, component, or feature |
62| `fix:` | Fixing a bug, broken style, or incorrect behavior |
63| `refactor:` | Restructuring code without changing functionality |
64| `style:` | CSS/SCSS/Tailwind adjustments only (no logic changes) |
65| `docs:` | Updating README, comments, or documentation files |
66| `chore:` | Dependency updates, config changes, build tweaks |
67| `content:` | Updating portfolio data in `portfolio.jsx` (text, images, links) |
68 
69Examples:
 
 
70 
71- `feat: add pricing calculator with WhatsApp checkout`
72- `fix: dark mode card background not applying on mobile`
73- `content: translate pricing section to English`
74- `chore: update framer-motion to v12`
75 
76## Library and dependency policy
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77 
78- **No hard restrictions** on library usage, but exercise caution:
79 - Avoid packages with known security vulnerabilities or a history of unmaintained releases with CVEs.
80 - Prefer well-maintained, widely-adopted libraries with active GitHub repositories and recent releases.
81 - Before adding a new dependency, ask: "Can this be done with what we already have?" The project already includes React 18, Framer Motion, Lottie React, FontAwesome, and react-icons — which cover most UI needs.
82- **Do not add backend frameworks, ORMs, or database drivers** — this is a static frontend only.
 
 
 
 
83 
84## Section visibility control
85 
86- Every major section in `src/portfolio.jsx` has a `display: true/false` flag.
87- Containers in `src/containers/` must check this flag and return `null` when `display` is false.
88- The header navigation in `src/components/header/Header.jsx` should also check these flags to show/hide nav links accordingly.
89- When adding a new section, follow the pattern: config object → container with display check → registration in `Main.jsx` → optional nav link.
 
 
 
 
 
 
 
90 
91## Build and deployment notes
92 
93- **Dev server:** `npm run start` → Vite on port 3000, auto-opens browser.
94- **Production build:** `npm run build` → outputs to `build/` directory.
95- **Deployment:** GitHub Actions workflow (`.github/workflows/deploy.yml`) triggers on push to `master`, builds, and deploys to `gh-pages` branch. Also runs weekly on Monday 12:00 UTC.
96- **CI variable:** `CI=false` is set in the workflow to prevent treating warnings as errors during build.
97- **Node version:** 18.x (locked in CI workflow).
98 
99## Reusable component structure
100 
101When creating a new reusable component in `src/components/`:
102 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103```
104src/components/MyComponent/
105├── MyComponent.jsx # Component logic
106└── MyComponent.scss # Styles (only if Tailwind is insufficient)
 
 
 
 
 
 
107```
108 
109- Component file uses PascalCase matching the folder name.
110- Accept `className` prop to allow parent styling overrides.
111- Use `export default function ComponentName(...)` pattern (consistent with existing codebase).
112 
113## Accessibility baseline
 
 
 
 
 
 
 
114 
115- Use semantic HTML elements (`<section>`, `<nav>`, `<button>`, `<h1>`-`<h6>`).
116- Ensure header navigation links correspond to section `id` attributes for anchor-based scrolling.
117- Images should include meaningful `alt` text.
118- Interactive elements must be keyboard-accessible (buttons, not divs with onClick).
119 
120## Internationalization note
121 
122- The website currently supports a single language (English, with some Indonesian pending revision).
123- No i18n framework is in use. If multi-language support is added in the future, use the `feat_lang` add-on structure already defined in `pricingSection.featuresList` as a starting point (ID/EN, 2 languages).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124 
BryaanF/LiantPortfolio · .clinerules/ui-standards.md
@@ +1 @@
1# Liant Portfolio - UI Standards & Component Patterns
2 
3> **Purpose:** Ensures all UI code (new and existing) follows consistent visual patterns, animation conventions, and component APIs. Read this before creating or modifying any UI code.
4 
5---
6 
7## 1. COLOR SYSTEM
 
 
 
8 
9### 1.1 Brand Gold (Primary Accent)
10 
11| Token | Hex | Usage |
12| ------------------------- | ----------------------- | ------------------------------------------------------------------------ |
13| `$brand-gold` / `#a1902e` | `#a1902e` | Buttons, links, highlights, active states, badges, decorative underlines |
14| `$brand-gold-hover` | Computed (10% lighter) | Button hover states |
15| `var(--btn-primary-bg)` | `#a1902e` (both themes) | CSS custom property for Tailwind arbitrary values |
16 
17**Critical:** The gold in `tailwind.config.js` must always match `variables.scss` -> `$brand-gold`. Currently both use `#a1902e`.
18 
19### 1.2 Theme Tokens (CSS Custom Properties)
 
 
 
20 
21Always use these in JSX via Tailwind arbitrary values — never hardcode colors:
22 
23```jsx
24// LIGHT MODE (.light-mode) — Neutral cool grey, no warm/pink tint
25--bg-body: #eaeaea
26--bg-card: #f5f5f5
27--bg-header: #f5f5f5
28--text-primary: #1a1a2e
29--text-secondary: #3a3a4e
30--border-light: rgba(0, 0, 0, 0.1)
31--shadow-card: 0 4px 20px rgba(0,0,0,0.05), 0 1px 6px rgba(0,0,0,0.07)
 
32 
33// DARK MODE (.dark-mode, [data-theme='dark'])
34--bg-body: #0a0a0a
35--bg-card: #1f1f1f
36--bg-header: #161b22
37--text-primary: #ffffff
38--text-secondary: #b0b0b0
39--border-light: rgba(255, 255, 255, 0.1)
40--shadow-card: 0 4px 6px rgba(255, 255, 255, 0.05)
41```
42 
43### 1.3 Tailwind Arbitrary Value Pattern
 
 
 
 
 
44 
45```jsx
46bg-[var(--bg-body)] // Section backgrounds
47bg-[var(--bg-card)] // Card backgrounds
48text-[var(--text-primary)] // Main heading text
49text-[var(--text-secondary)] // Subtitle, metadata, description text
50border-[var(--border-light)] // Subtle borders and dividers
51text-[var(--btn-primary-bg)] // Brand accent text
52bg-[var(--btn-primary-bg)] // Primary button fill
53hover:text-[var(--btn-primary-bg)] // Link/button hover accent
54hover:border-[var(--btn-primary-bg)] // Card hover border accent
55```
56 
57---
 
 
 
58 
59## 2. SECTION LAYOUT PATTERN
60 
61Every section follows this exact structure in JSX:
62 
63```jsx
64import {motion} from "framer-motion";
65import LanguageContext from "../../contexts/LanguageContext";
66import {getTranslation} from "../../utils/translations";
 
 
 
 
 
67 
68export default function MySection() {
69 const {lang} = useContext(LanguageContext);
70 const {isDark} = useContext(StyleContext); // if theme needed
71 
72 if (!config.display) return null;
 
 
 
73 
74 return (
75 <section
76 id="my-section"
77 className="relative py-16 md:py-24 overflow-hidden"
78 style={{backgroundColor: "var(--bg-body)"}}
79 >
80 <div className="max-w-7xl mx-auto px-4 md:px-8">
81 {/* Header: centered, animated */}
82 <motion.div
83 initial={{opacity: 0, y: 20}}
84 whileInView={{opacity: 1, y: 0}}
85 viewport={{once: true}}
86 transition={{duration: 0.6}}
87 className="text-center mb-12 md:mb-16"
88 >
89 <h2 className="text-3xl md:text-5xl font-black text-[var(--text-primary)] mb-4">
90 {getTranslation(config.title, lang)}
91 </h2>
92 <p
93 className="text-sm md:text-base uppercase tracking-[0.2em] font-semibold"
94 style={{color: "var(--text-secondary)"}}
95 >
96 {getTranslation(config.subtitle, lang)}
97 </p>
98 </motion.div>
99 
100 {/* Content grid */}
101 <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
102 {/* ... cards / content ... */}
103 </div>
104 </div>
105 </section>
106 );
107}
108```
109 
110### Section IDs (used by header nav anchors)
111 
112| id | Container |
113| --------------- | ------------------ |
114| `#greeting` | Greeting.jsx |
115| `#intro-video` | IntroVideo.jsx |
116| `#skills` | Skills.jsx |
117| `#education` | Education.jsx |
118| `#experience` | WorkExperience.jsx |
119| `#projects` | BigProjects.jsx |
120| `#achievements` | Achievement.jsx |
121| `#pricing` | Pricing.jsx |
122| `#contact` | Contact.jsx |
123 
124---
125 
126## 3. ANIMATION CONVENTIONS
 
 
 
 
127 
128### 3.1 Scroll-Triggered Reveal (Framer Motion)
129 
130Use `whileInView` for all scroll-triggered animations (not `animate`, which runs on mount):
131 
132```jsx
133// Single element
134<motion.div
135 initial={{opacity: 0, y: 20}}
136 whileInView={{opacity: 1, y: 0}}
137 viewport={{once: true, margin: "-100px"}}
138 transition={{duration: 0.6}}
139>
140 ...
141</motion.div>;
142 
143// Staggered children
144const containerVariants = {
145 hidden: {opacity: 0},
146 visible: {
147 opacity: 1,
148 transition: {staggerChildren: 0.15, delayChildren: 0.2}
149 }
150};
151const itemVariants = {
152 hidden: {opacity: 0, y: 12},
153 visible: {opacity: 1, y: 0, transition: {duration: 0.4}}
154};
155 
156<motion.div
157 variants={containerVariants}
158 initial="hidden"
159 whileInView="visible"
160 viewport={{once: true}}
161>
162 {items.map((item, i) => (
163 <motion.div key={i} variants={itemVariants}>
164 ...
165 </motion.div>
166 ))}
167</motion.div>;
168```
169 
170### 3.2 Hover Animations
171 
172```jsx
173// Card lift on hover
174<motion.div whileHover={{y: -5}}>...</motion.div>;
175 
176// Button/link with CSS transition
177className = "transition-all duration-300 hover:-translate-y-1";
178```
179 
180### 3.3 Floating/Looping Animation (for decorative elements)
 
 
181 
182```jsx
183<motion.span
184 animate={{y: [0, -6, 0]}}
185 transition={{duration: 3, repeat: Infinity, ease: "easeInOut"}}
186>
187 🚀
188</motion.span>
189```
190 
191---
 
 
 
192 
193## 4. TYPOGRAPHY CONVENTIONS
194 
195| Element | Tailwind classes | Style |
196| ---------------- | -------------------------------------------------------------------------------------------- | ------------------------------- |
197| Section heading | `text-3xl md:text-5xl font-black text-[var(--text-primary)]` | Black weight, responsive sizing |
198| Section subtitle | `text-sm md:text-base uppercase tracking-[0.2em] font-semibold text-[var(--text-secondary)]` | Uppercase, letter-spaced |
199| Card title | `text-lg md:text-xl font-bold text-[var(--text-primary)]` | Bold weight |
200| Card description | `text-sm leading-relaxed text-[var(--text-secondary)]` | Normal weight |
201| Pill/badge | `text-[9px] font-black px-2 py-1 rounded-full` | Tiny uppercase |
202| Duration/tags | `text-[10px] font-black tracking-[0.2em] uppercase` | Small uppercase |
203 
204---
205 
206## 5. SECTION HEADER PATTERN (STANDARDIZED)
207 
208All sections must use the shared `SectionHeader` component located at `src/components/sectionHeader/SectionHeader.jsx`. This ensures visual harmony and consistency across the portfolio.
209 
210### Usage
211 
212```jsx
213import SectionHeader from "../../components/sectionHeader/SectionHeader";
214 
215<SectionHeader
216 title="Section Title" // Required
217 subtitle="Optional subtitle" // Optional
218 emoji="🚀" // Optional emoji above title
219 align="center" // "center" (default) | "left"
220/>;
221```
222 
223### Component API
224 
225| Prop | Type | Default | Description |
226| ---------- | -------------------- | ---------- | ----------------------------------------- |
227| `title` | string | (required) | Section heading text |
228| `subtitle` | string | `""` | Subtitle shown below gold accent strip |
229| `emoji` | string | `""` | Optional emoji/icon displayed above title |
230| `align` | `"center"`\|`"left"` | `"center"` | Text alignment |
231 
232### Rendered Output
233 
234- **Title**: `text-3xl md:text-5xl font-bold text-[var(--text-primary)]`
235- **Emoji** (optional): `text-4xl md:text-5xl` above title
236- **Gold underline accent strip**: Animated `width: 0 → 80px` with `bg-[var(--btn-primary-bg)]`
237- **Subtitle** (optional): `text-sm md:text-base uppercase tracking-[0.2em] font-semibold text-[var(--text-secondary)]`
238 
239### Example Usage by Section
240 
241| Container | title | subtitle | emoji | align |
242| -------------- | -------------------------- | ----------------------------- | ----- | ------ |
243| Education | `educationInfo.title` | — | `🎓` | center |
244| Skills | `skillsSection.title` | `skillsSection.subTitle` | `💡` | center |
245| IntroVideo | `introVideo.title` | `introVideo.subtitle` | `🎬` | center |
246| WorkExperience | `workExperiences.title` | `workExperiences.subtitle` | `💼` | center |
247| BigProjects | `bigProjects.title` | `bigProjects.subtitle` | `🚀` | center |
248| Achievement | `achievementSection.title` | `achievementSection.subtitle` | `🏆` | center |
249| Pricing | `pricingSection.title` | `pricingSection.description` | `💰` | center |
250| Contact | `contactInfo.title` | `contactInfo.subtitle` | `✉️` | center |
251 
252---
253 
254## 6. CARD STANDARDS
255 
256### 6.1 Card Container
257 
258```jsx
259<div className="rounded-xl border border-[var(--border-light)] bg-[var(--bg-card)]
260 transition-all duration-300 hover:-translate-y-1 hover:shadow-xl">
261```
262 
263### 6.2 Card Hover Border Accent
264 
265```jsx
266// Add group class to parent and use:
267className =
268 "group border border-[var(--border-light)] hover:border-[var(--btn-primary-bg)]/50 transition-colors";
269```
270 
271### 6.3 Bullet/List Items
272 
273```jsx
274// Gold dot bullet
275<li className="flex gap-2">
276 <span className="mt-[6px] h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--btn-primary-bg)]" />
277 <span className="text-[var(--text-secondary)]">{text}</span>
278</li>
279 
280// Gold triangle bullet
281<li className="flex items-start gap-3">
282 <span className="text-[var(--btn-primary-bg)] mt-1.5 shrink-0 text-[8px]">▶</span>
283 <span className="text-[var(--text-secondary)]">{text}</span>
284</li>
285```
286 
287### 6.4 Spec/Info Grid (2-column metadata)
288 
289```jsx
290<div
291 className="grid grid-cols-2 gap-2 text-[10px] p-3 rounded-lg border
292 border-[var(--border-light)]"
293 style={{
294 backgroundColor: isDark ? "rgba(255,255,255,0.02)" : "rgba(0,0,0,0.02)"
295 }}
296>
297 {/* Items */}
298</div>
299```
300 
301---
302 
303## 7. BUTTON PATTERNS
304 
305### 7.1 Primary Button (Gold fill)
306 
307```jsx
308<button
309 className="px-6 py-3 rounded-lg text-xs font-black uppercase tracking-widest
310 transition-all hover:brightness-110 active:scale-95"
311 style={{backgroundColor: "var(--btn-primary-bg)", color: "#fff"}}
312>
313 {text}
314</button>
315```
316 
317### 7.2 Secondary/Outline Button
318 
319```jsx
320<button
321 className="py-3 rounded-lg border border-[var(--border-light)] text-xs
322 font-bold transition-colors"
323 style={{color: "var(--text-primary)", backgroundColor: "transparent"}}
324>
325 {text}
326</button>
327```
328 
329### 7.3 Danger/Remove Button
330 
331```jsx
332<button
333 className="py-3 rounded-lg text-xs font-bold uppercase tracking-widest"
334 style={{
335 color: "#f87171",
336 backgroundColor: "rgba(239,68,68,0.08)",
337 border: "1px solid rgba(239,68,68,0.25)"
338 }}
339>
340 {text}
341</button>
342```
343 
344### 7.4 Legacy `.main-button` (used by Button.jsx component)
345 
346- Defined in `src/components/button/Button.scss`
347- Gold background, white text, rounded, hover lift effect
348- For NEW code, prefer primary button pattern above
349 
350---
351 
352## 8. SVG ICON PATTERN
353 
354Use either approach consistently. For new code, prefer **inline SVG components** (portable, no external dependency).
355 
356### 8.1 Inline SVGs (Recommended for new code)
357 
358```jsx
359const MyIcon = () => (
360 <svg
361 width="14"
362 height="14"
363 viewBox="0 0 24 24"
364 fill="none"
365 stroke="currentColor"
366 strokeWidth="2"
367 >
368 <circle cx="12" cy="12" r="10" />
369 <path d="M12 6v6l4 2" />
370 </svg>
371);
372```
373 
374Usage: `<MyIcon />` — color inherits from `currentColor`.
375 
376### 8.2 FontAwesome (Used in SocialMedia, Contact, header)
377 
378- Import: `import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"`
379- Brand icons: `import { faGithub, faLinkedin } from "@fortawesome/free-brands-svg-icons"`
380- Solid icons: `import { faEnvelope } from "@fortawesome/free-solid-svg-icons"`
381- Brand colors defined as SCSS variables in `variables.scss` (e.g., `$linkedin: #0e76a8`)
382 
383### 8.3 React Icons (Installed but unused — avoid if possible)
384 
385- The `react-icons` package is installed but not currently used in any component.
386- Prefer inline SVGs or FontAwesome instead.
387 
388---
389 
390## 9. FORM BLUEPRINT (WhatsApp/Email Actions)
391 
392**Do NOT use `<form>` elements.** All interactions are direct links:
393 
394```jsx
395// WhatsApp
396<a href={`https://wa.me/6281331487753?text=${encodeURIComponent(message)}`}
397 target="_blank" rel="noopener noreferrer">
398 Contact via WhatsApp
399</a>
400 
401// Email
402<a href={`mailto:briliantfikri@gmail.com?subject=${subject}&body=${body}`}>
403 Send Email
404</a>
405```
406 
407For the pricing checkout message, construct the message template inside a `handleCheckout` function and call `window.open()`.
408 
409---
410 
411## 10. IMAGE HANDLING (Vite)
412 
413- **Always import images statically** (Vite convention):
414 ```jsx
415 import myImage from "../../assets/images/myImage.png";
416 // Usage: <img src={myImage} alt="..." />
417 ```
418- **Do NOT use `require()`** — that is a CRA/Webpack pattern. The Skills.jsx container is the only file still using `require()` and should be migrated.
419- For images from external URLs, use `src={url}` directly (no import needed).
420- Use `loading="lazy"` on below-the-fold images.
421 
422---
423 
424## 11. LANGUAGE / I18N PATTERN
425 
426All user-facing text that supports bilingual display uses this pattern:
427 
4281. In `portfolio.jsx`: define as `{ en: "...", id: "..." }` object.
4292. In component: import `LanguageContext`, get `lang`, call `getTranslation(configField, lang)`.
4303. The helper lives in `src/utils/translations.js`.
431 
432```jsx
433import LanguageContext from "../../contexts/LanguageContext";
434import {getTranslation} from "../../utils/translations";
435 
436const {lang} = useContext(LanguageContext);
437const title = getTranslation(config.title, lang); // returns string
438```
439 
440**Rules:**
441 
442- `getTranslation()` handles nested objects recursively (e.g., `workflow` steps).
443- Arrays of bilingual objects are resolved element-by-element.
444- Plain strings/numbers pass through unchanged.
445 
446---
447 
448## 12. MODAL / OVERLAY PATTERNS
449 
450### 12.1 ImageLightbox (`src/components/imageLightbox/ImageLightbox.jsx`)
451 
452```jsx
453<ImageLightbox src={url} alt={text} onClose={() => setState(null)} />
454```
455 
456- Fixed overlay, dark backdrop, centered image, close on backdrop click or ✕ button.
457- Always wrap with `{show && <ImageLightbox ... />}`.
458 
459### 12.2 ProjectShowcase (`src/components/projectShowcase/ProjectShowcase.jsx`)
460 
461```jsx
462<ProjectShowcase
463 title={string}
464 description={string}
465 media={[{type: "image" | "video", url, caption, thumbnail}]}
466 externalUrl={
467 string
468 } /* Optional — shows "Visit Website" button in top-right header */
469 onClose={fn}
470/>
471```
472 
473- Full-screen dark overlay with media gallery.
474- **"Visit Website"** button appears in top-right header ONLY when `externalUrl` is provided.
475- Used by AchievementCard and BigProject containers.
476 
477---
478 
479## 13. THEME TOGGLE SYSTEM
480 
481- Global context: `src/contexts/StyleContext.js` provides `{ isDark, changeTheme }`.
482- Theme is persisted in `localStorage` via `useLocalStorage` hook.
483- Toggle switch: `src/components/ToggleSwitch/ToggleSwitch.jsx` (sun/moon emoji).
484- Theme class: `.light-mode` or `.dark-mode` on root `<div>` in `Main.jsx`.
485- CSS variables defined in `src/variables.scss` respond to these classes.
486 
487```jsx
488import StyleContext from "../../contexts/StyleContext";
489const { isDark } = useContext(StyleContext);
490 
491// Use in conditional styling:
492<div style={{ backgroundColor: isDark ? "rgba(0,0,0,0.4)" : "rgba(255,255,255,0.6)" }}>
493```
494 
495---
496 
497## 14. LOTTIE ANIMATION PATTERN
498 
499```jsx
500import DisplayLottie from "../../components/displayLottie/DisplayLottie";
501import animationData from "../../assets/lottie/myAnimation";
502 
503{
504 illustration.animated ? (
505 <DisplayLottie animationData={animationData} />
506 ) : (
507 <img src={fallbackImage} alt="..." />
508 );
509}
510```
511 
512- All Lottie JSON files live in `src/assets/lottie/`.
513- The `DisplayLottie` component wraps `lottie-react` with `Suspense` + `<Loading>` fallback.
514 
515---
516 
517## 15. FRAMER MOTION IMPORTS
518 
519Always import only what's needed from `framer-motion`:
520 
521```jsx
522import {motion, AnimatePresence} from "framer-motion";
523```
524 
525- `motion.div`, `motion.button`, `motion.a`, `motion.span`, `motion.img`, `motion.h1`-`h6` supported.
526- `AnimatePresence` for mounting/unmounting animations (modals, checkout bar).
527 
528---
529 
530## 16. KNOWN INCONSISTENCIES & MIGRATION NOTES
531 
532These are NOT required to fix immediately, but be aware of them when touching related code:
533 
5341. **`Skills.jsx` uses `require()` for static images** — should use `import` like all other containers (Vite pattern).
5352. **`Button.jsx` does not spread `className`** — it wraps in `<div className={className}>` instead of applying directly to `<a>`. New button code should use the inline primary button pattern instead.
5363. **`variables.scss` has legacy SCSS variables** that duplicate CSS custom properties (e.g., `$textColorDark` is `#ffffff` but already covered by `--text-primary` in dark mode). When refactoring, prefer CSS custom properties.
5374. **`Greeting.scss` and `Skills.scss` still contain legacy classes** that are partially overridden by Tailwind. When editing, prefer moving styles to Tailwind and removing SCSS.
5385. **`Header.scss` is partially refactored** — it has both legacy classes and a comment noting Tailwind removal was done. When editing, complete the migration.
5396. **`SplashScreen.jsx` uses `.css` instead of `.scss`** — minor inconsistency.
5407. **`ProjectShowcase.jsx` and `Pricing.jsx` both define duplicate inline SVG icons** (`Close`, `Image`) — consider extracting to `src/utils/icons.jsx` in the future.
541 
542---
543 
544## 17. FILE ORGANIZATION SUMMARY
545 
546```
547src/
548├── portfolio.jsx # Single config file — all content data
549├── App.jsx # Root component
550├── Main.jsx # Container orchestrator, theme/language providers
551├── variables.scss # Design tokens (CSS vars + SCSS vars)
552├── contexts/
553│ ├── StyleContext.js # Theme state (isDark, changeTheme)
554│ └── LanguageContext.js # Language state (lang, changeLang)
555├── hooks/
556│ └── useLocalStorage.js # Persistent state hook
557├── utils/
558│ └── translations.js # getTranslation() helper
559├── components/ # Reusable UI components
560│ ├── header/Header.jsx
561│ ├── button/Button.jsx
562│ ├── footer/Footer.jsx
563│ ├── socialMedia/SocialMedia.jsx
564│ ├── ToggleSwitch/ToggleSwitch.jsx
565│ ├── LanguageToggle/LanguageToggle.jsx
566│ ├── displayLottie/DisplayLottie.jsx
567│ ├── imageLightbox/ImageLightbox.jsx
568│ ├── projectShowcase/ProjectShowcase.jsx
569│ ├── achievementCard/AchievementCard.jsx
570│ ├── educationCard/EducationCard.jsx
571│ ├── experienceCard/ExperienceCard.jsx
572│ ├── softwareSkills/SoftwareSkill.jsx
573│ └── ... (other legacy components)
574├── containers/ # Page sections (one per portfolio section)
575│ ├── greeting/Greeting.jsx
576│ ├── introVideo/IntroVideo.jsx
577│ ├── skills/Skills.jsx
578│ ├── education/Education.jsx
579│ ├── workExperience/WorkExperience.jsx
580│ ├── projects/Projects.jsx
581│ ├── BigProjects/BigProject.jsx
582│ ├── achievement/Achievement.jsx
583│ ├── pricing/Pricing.jsx
584│ ├── contact/Contact.jsx
585│ ├── splashScreen/SplashScreen.jsx
586│ └── ... (others)
587└── assets/
588 ├── images/ # Static images (imported in portfolio.jsx)
589 ├── lottie/ # Lottie JSON animations
590 └── fonts/ # Custom fonts
591```
592 
@@ −1 +1 @@
1−# Liant Portfolio - Project Guidelines
1+# Liant Portfolio - UI Standards & Component Patterns
22  
3−> **Compatibility Note:** This guideline file is designed to be tool-agnostic and readable by any AI coding assistant (Cline, Copilot, future tools). It supplements the existing [.github/copilot-instructions.md](../.github/copilot-instructions.md) with additional conventions and preferences not covered there.
3+> **Purpose:** Ensures all UI code (new and existing) follows consistent visual patterns, animation conventions, and component APIs. Read this before creating or modifying any UI code.
44  
5−## Brief overview
5+---
66  
7−- **Project:** A data-driven, single-page portfolio website for Brilliant Fikri (Liant), built with React 18 + Vite + Tailwind CSS + SCSS.
8−- **Nature:** Pure frontend — no backend, no database. All content lives in `src/portfolio.jsx` as a single configuration file.
9−- **Deployment:** Static site deployed to GitHub Pages (`gh-pages` branch) via GitHub Actions from `master` branch.
10−- **Domain:** [brilliantfikri.com](https://brilliantfikri.com)
7+## 1. COLOR SYSTEM
118  
12−## Site language policy
9+### 1.1 Brand Gold (Primary Accent)
1310  
14−- **Primary language for all user-facing content is English.** The portfolio targets an international/professional audience.
15−- **Pending revision:** `pricingSection` in `src/portfolio.jsx` is currently in Bahasa Indonesia and must be translated to English. This includes all `title`, `description`, `desc`, `longDesc`, `features`, `notIncluded`, `workflow` fields, and the WhatsApp checkout message template in `src/containers/pricing/Pricing.jsx` (`handleCheckout` function).
16−- Comments in code may use either English or Bahasa Indonesia — both are acceptable.
17−- Communication with the developer (AI or human) may use Bahasa Indonesia.
11+| Token | Hex | Usage |
12+| ------------------------- | ----------------------- | ------------------------------------------------------------------------ |
13+| `$brand-gold` / `#a1902e` | `#a1902e` | Buttons, links, highlights, active states, badges, decorative underlines |
14+| `$brand-gold-hover` | Computed (10% lighter) | Button hover states |
15+| `var(--btn-primary-bg)` | `#a1902e` (both themes) | CSS custom property for Tailwind arbitrary values |
1816  
19−## Communication style
17+**Critical:** The gold in `tailwind.config.js` must always match `variables.scss` -> `$brand-gold`. Currently both use `#a1902e`.
2018  
21−- **Concise and direct.** Avoid filler words and conversational fluff in code comments, commit messages, and documentation.
22−- Code comments should explain the "why," not the "what" — the code itself should be self-documenting for what it does.
23−- **SCSS comments** use `/* */` block style for section headers (see existing pattern in `variables.scss`).
24−- **JSX comments:** `//` for inline, `/* */` for multi-line explanations.
19+### 1.2 Theme Tokens (CSS Custom Properties)
2520  
26−## Styling conventions
21+Always use these in JSX via Tailwind arbitrary values — never hardcode colors:
2722  
28−- **Tailwind CSS is the primary styling approach** for all new components and sections. Use utility classes directly in JSX (reference `src/containers/pricing/Pricing.jsx` as the canonical example).
29−- **SCSS is reserved for reusable custom components** that need encapsulated, complex styles not easily expressed with Tailwind utilities. When creating a new reusable component in `src/components/`, co-locate a `.scss` file in the same folder.
30−- **Design tokens:** Always reference CSS custom properties for theme-switchable values:
31− - `var(--bg-body)`, `var(--bg-card)`, `var(--text-primary)`, `var(--text-secondary)`, `var(--btn-primary-bg)`, `var(--border-light)`
32− - These are defined in `src/variables.scss` for both `.light-mode` and `.dark-mode`.
33− - For Tailwind, use arbitrary value syntax when needed: `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`, `border-[var(--border-light)]`.
34−- **SCSS variables** (prefixed with `$`) are for non-theme values only (e.g., `$brand-gold`, `$buttonHover`, static colors). Do not use SCSS variables for colors that should respond to dark/light mode.
35−- **Never hardcode colors** in component JSX or SCSS that should respect the theme. Always go through CSS custom properties.
36−- **Brand color:** Gold `#a1902e` (`$brand-gold`). This is the primary accent across buttons, links, and highlights.
37−- **IMPORTANT:** The brand-gold in `tailwind.config.js` must match `$brand-gold` in `variables.scss`. Currently both use `#a1902e`. If one changes, update the other.
23+```jsx
24+// LIGHT MODE (.light-mode) — Neutral cool grey, no warm/pink tint
25+--bg-body: #eaeaea
26+--bg-card: #f5f5f5
27+--bg-header: #f5f5f5
28+--text-primary: #1a1a2e
29+--text-secondary: #3a3a4e
30+--border-light: rgba(0, 0, 0, 0.1)
31+--shadow-card: 0 4px 20px rgba(0,0,0,0.05), 0 1px 6px rgba(0,0,0,0.07)
3832  
39−## Input and form handling
33+// DARK MODE (.dark-mode, [data-theme='dark'])
34+--bg-body: #0a0a0a
35+--bg-card: #1f1f1f
36+--bg-header: #161b22
37+--text-primary: #ffffff
38+--text-secondary: #b0b0b0
39+--border-light: rgba(255, 255, 255, 0.1)
40+--shadow-card: 0 4px 6px rgba(255, 255, 255, 0.05)
41+```
4042  
41−- **This project has no backend.** There is no server to POST form data to.
42−- All "contact" or "checkout" interactions must use **direct action links:**
43− - **WhatsApp:** `https://wa.me/6281331487753?text=...` with a pre-formatted, URL-encoded message.
44− - **Email:** `mailto:briliantfikri@gmail.com` with optional `?subject=...&body=...` parameters.
45−- Do NOT create `<form>` elements with submit handlers — there is nothing to handle submissions.
46−- Do NOT add any form validation libraries or backend integration code.
43+### 1.3 Tailwind Arbitrary Value Pattern
4744  
48−## State management
45+```jsx
46+bg-[var(--bg-body)] // Section backgrounds
47+bg-[var(--bg-card)] // Card backgrounds
48+text-[var(--text-primary)] // Main heading text
49+text-[var(--text-secondary)] // Subtitle, metadata, description text
50+border-[var(--border-light)] // Subtle borders and dividers
51+text-[var(--btn-primary-bg)] // Brand accent text
52+bg-[var(--btn-primary-bg)] // Primary button fill
53+hover:text-[var(--btn-primary-bg)] // Link/button hover accent
54+hover:border-[var(--btn-primary-bg)] // Card hover border accent
55+```
4956  
50−- **Context API** is the established pattern for global state (see `src/contexts/StyleContext.js` for theme).
51−- **Local state** (`useState` / `useReducer`) is sufficient for component-specific state.
52−- **Custom hooks** in `src/hooks/` for reusable state logic (e.g., `useLocalStorage` for persistence).
53−- Do NOT introduce Redux, Zustand, or other external state management libraries — they are unnecessary for this project's complexity level.
57+---
5458  
55−## Commit message conventions
59+## 2. SECTION LAYOUT PATTERN
5660  
57−Use structured, prefixed commit messages. The format is: `<type>: <brief description>`
61+Every section follows this exact structure in JSX:
5862  
59−| Prefix | Use when |
60−| ----------- | ---------------------------------------------------------------- |
61−| `feat:` | Adding a new section, component, or feature |
62−| `fix:` | Fixing a bug, broken style, or incorrect behavior |
63−| `refactor:` | Restructuring code without changing functionality |
64−| `style:` | CSS/SCSS/Tailwind adjustments only (no logic changes) |
65−| `docs:` | Updating README, comments, or documentation files |
66−| `chore:` | Dependency updates, config changes, build tweaks |
67−| `content:` | Updating portfolio data in `portfolio.jsx` (text, images, links) |
63+```jsx
64+import {motion} from "framer-motion";
65+import LanguageContext from "../../contexts/LanguageContext";
66+import {getTranslation} from "../../utils/translations";
6867  
69−Examples:
68+export default function MySection() {
69+ const {lang} = useContext(LanguageContext);
70+ const {isDark} = useContext(StyleContext); // if theme needed
7071  
71−- `feat: add pricing calculator with WhatsApp checkout`
72−- `fix: dark mode card background not applying on mobile`
73−- `content: translate pricing section to English`
74−- `chore: update framer-motion to v12`
72+ if (!config.display) return null;
7573  
76−## Library and dependency policy
74+ return (
75+ <section
76+ id="my-section"
77+ className="relative py-16 md:py-24 overflow-hidden"
78+ style={{backgroundColor: "var(--bg-body)"}}
79+ >
80+ <div className="max-w-7xl mx-auto px-4 md:px-8">
81+ {/* Header: centered, animated */}
82+ <motion.div
83+ initial={{opacity: 0, y: 20}}
84+ whileInView={{opacity: 1, y: 0}}
85+ viewport={{once: true}}
86+ transition={{duration: 0.6}}
87+ className="text-center mb-12 md:mb-16"
88+ >
89+ <h2 className="text-3xl md:text-5xl font-black text-[var(--text-primary)] mb-4">
90+ {getTranslation(config.title, lang)}
91+ </h2>
92+ <p
93+ className="text-sm md:text-base uppercase tracking-[0.2em] font-semibold"
94+ style={{color: "var(--text-secondary)"}}
95+ >
96+ {getTranslation(config.subtitle, lang)}
97+ </p>
98+ </motion.div>
7799  
78−- **No hard restrictions** on library usage, but exercise caution:
79− - Avoid packages with known security vulnerabilities or a history of unmaintained releases with CVEs.
80− - Prefer well-maintained, widely-adopted libraries with active GitHub repositories and recent releases.
81− - Before adding a new dependency, ask: "Can this be done with what we already have?" The project already includes React 18, Framer Motion, Lottie React, FontAwesome, and react-icons — which cover most UI needs.
82−- **Do not add backend frameworks, ORMs, or database drivers** — this is a static frontend only.
100+ {/* Content grid */}
101+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
102+ {/* ... cards / content ... */}
103+ </div>
104+ </div>
105+ </section>
106+ );
107+}
108+```
83109  
84−## Section visibility control
110+### Section IDs (used by header nav anchors)
85111  
86−- Every major section in `src/portfolio.jsx` has a `display: true/false` flag.
87−- Containers in `src/containers/` must check this flag and return `null` when `display` is false.
88−- The header navigation in `src/components/header/Header.jsx` should also check these flags to show/hide nav links accordingly.
89−- When adding a new section, follow the pattern: config object → container with display check → registration in `Main.jsx` → optional nav link.
112+| id | Container |
113+| --------------- | ------------------ |
114+| `#greeting` | Greeting.jsx |
115+| `#intro-video` | IntroVideo.jsx |
116+| `#skills` | Skills.jsx |
117+| `#education` | Education.jsx |
118+| `#experience` | WorkExperience.jsx |
119+| `#projects` | BigProjects.jsx |
120+| `#achievements` | Achievement.jsx |
121+| `#pricing` | Pricing.jsx |
122+| `#contact` | Contact.jsx |
90123  
91−## Build and deployment notes
124+---
92125  
93−- **Dev server:** `npm run start` → Vite on port 3000, auto-opens browser.
94−- **Production build:** `npm run build` → outputs to `build/` directory.
95−- **Deployment:** GitHub Actions workflow (`.github/workflows/deploy.yml`) triggers on push to `master`, builds, and deploys to `gh-pages` branch. Also runs weekly on Monday 12:00 UTC.
96−- **CI variable:** `CI=false` is set in the workflow to prevent treating warnings as errors during build.
97−- **Node version:** 18.x (locked in CI workflow).
126+## 3. ANIMATION CONVENTIONS
98127  
99−## Reusable component structure
128+### 3.1 Scroll-Triggered Reveal (Framer Motion)
100129  
101−When creating a new reusable component in `src/components/`:
130+Use `whileInView` for all scroll-triggered animations (not `animate`, which runs on mount):
102131  
132+```jsx
133+// Single element
134+<motion.div
135+ initial={{opacity: 0, y: 20}}
136+ whileInView={{opacity: 1, y: 0}}
137+ viewport={{once: true, margin: "-100px"}}
138+ transition={{duration: 0.6}}
139+>
140+ ...
141+</motion.div>;
142+ 
143+// Staggered children
144+const containerVariants = {
145+ hidden: {opacity: 0},
146+ visible: {
147+ opacity: 1,
148+ transition: {staggerChildren: 0.15, delayChildren: 0.2}
149+ }
150+};
151+const itemVariants = {
152+ hidden: {opacity: 0, y: 12},
153+ visible: {opacity: 1, y: 0, transition: {duration: 0.4}}
154+};
155+ 
156+<motion.div
157+ variants={containerVariants}
158+ initial="hidden"
159+ whileInView="visible"
160+ viewport={{once: true}}
161+>
162+ {items.map((item, i) => (
163+ <motion.div key={i} variants={itemVariants}>
164+ ...
165+ </motion.div>
166+ ))}
167+</motion.div>;
103168 ```
104−src/components/MyComponent/
105−├── MyComponent.jsx # Component logic
106−└── MyComponent.scss # Styles (only if Tailwind is insufficient)
169+ 
170+### 3.2 Hover Animations
171+ 
172+```jsx
173+// Card lift on hover
174+<motion.div whileHover={{y: -5}}>...</motion.div>;
175+ 
176+// Button/link with CSS transition
177+className = "transition-all duration-300 hover:-translate-y-1";
107178 ```
108179  
109−- Component file uses PascalCase matching the folder name.
110−- Accept `className` prop to allow parent styling overrides.
111−- Use `export default function ComponentName(...)` pattern (consistent with existing codebase).
180+### 3.3 Floating/Looping Animation (for decorative elements)
112181  
113−## Accessibility baseline
182+```jsx
183+<motion.span
184+ animate={{y: [0, -6, 0]}}
185+ transition={{duration: 3, repeat: Infinity, ease: "easeInOut"}}
186+>
187+ 🚀
188+</motion.span>
189+```
114190  
115−- Use semantic HTML elements (`<section>`, `<nav>`, `<button>`, `<h1>`-`<h6>`).
116−- Ensure header navigation links correspond to section `id` attributes for anchor-based scrolling.
117−- Images should include meaningful `alt` text.
118−- Interactive elements must be keyboard-accessible (buttons, not divs with onClick).
191+---
119192  
120−## Internationalization note
193+## 4. TYPOGRAPHY CONVENTIONS
121194  
122−- The website currently supports a single language (English, with some Indonesian pending revision).
123−- No i18n framework is in use. If multi-language support is added in the future, use the `feat_lang` add-on structure already defined in `pricingSection.featuresList` as a starting point (ID/EN, 2 languages).
195+| Element | Tailwind classes | Style |
196+| ---------------- | -------------------------------------------------------------------------------------------- | ------------------------------- |
197+| Section heading | `text-3xl md:text-5xl font-black text-[var(--text-primary)]` | Black weight, responsive sizing |
198+| Section subtitle | `text-sm md:text-base uppercase tracking-[0.2em] font-semibold text-[var(--text-secondary)]` | Uppercase, letter-spaced |
199+| Card title | `text-lg md:text-xl font-bold text-[var(--text-primary)]` | Bold weight |
200+| Card description | `text-sm leading-relaxed text-[var(--text-secondary)]` | Normal weight |
201+| Pill/badge | `text-[9px] font-black px-2 py-1 rounded-full` | Tiny uppercase |
202+| Duration/tags | `text-[10px] font-black tracking-[0.2em] uppercase` | Small uppercase |
203+ 
204+---
205+ 
206+## 5. SECTION HEADER PATTERN (STANDARDIZED)
207+ 
208+All sections must use the shared `SectionHeader` component located at `src/components/sectionHeader/SectionHeader.jsx`. This ensures visual harmony and consistency across the portfolio.
209+ 
210+### Usage
211+ 
212+```jsx
213+import SectionHeader from "../../components/sectionHeader/SectionHeader";
214+ 
215+<SectionHeader
216+ title="Section Title" // Required
217+ subtitle="Optional subtitle" // Optional
218+ emoji="🚀" // Optional emoji above title
219+ align="center" // "center" (default) | "left"
220+/>;
221+```
222+ 
223+### Component API
224+ 
225+| Prop | Type | Default | Description |
226+| ---------- | -------------------- | ---------- | ----------------------------------------- |
227+| `title` | string | (required) | Section heading text |
228+| `subtitle` | string | `""` | Subtitle shown below gold accent strip |
229+| `emoji` | string | `""` | Optional emoji/icon displayed above title |
230+| `align` | `"center"`\|`"left"` | `"center"` | Text alignment |
231+ 
232+### Rendered Output
233+ 
234+- **Title**: `text-3xl md:text-5xl font-bold text-[var(--text-primary)]`
235+- **Emoji** (optional): `text-4xl md:text-5xl` above title
236+- **Gold underline accent strip**: Animated `width: 0 → 80px` with `bg-[var(--btn-primary-bg)]`
237+- **Subtitle** (optional): `text-sm md:text-base uppercase tracking-[0.2em] font-semibold text-[var(--text-secondary)]`
238+ 
239+### Example Usage by Section
240+ 
241+| Container | title | subtitle | emoji | align |
242+| -------------- | -------------------------- | ----------------------------- | ----- | ------ |
243+| Education | `educationInfo.title` | — | `🎓` | center |
244+| Skills | `skillsSection.title` | `skillsSection.subTitle` | `💡` | center |
245+| IntroVideo | `introVideo.title` | `introVideo.subtitle` | `🎬` | center |
246+| WorkExperience | `workExperiences.title` | `workExperiences.subtitle` | `💼` | center |
247+| BigProjects | `bigProjects.title` | `bigProjects.subtitle` | `🚀` | center |
248+| Achievement | `achievementSection.title` | `achievementSection.subtitle` | `🏆` | center |
249+| Pricing | `pricingSection.title` | `pricingSection.description` | `💰` | center |
250+| Contact | `contactInfo.title` | `contactInfo.subtitle` | `✉️` | center |
251+ 
252+---
253+ 
254+## 6. CARD STANDARDS
255+ 
256+### 6.1 Card Container
257+ 
258+```jsx
259+<div className="rounded-xl border border-[var(--border-light)] bg-[var(--bg-card)]
260+ transition-all duration-300 hover:-translate-y-1 hover:shadow-xl">
261+```
262+ 
263+### 6.2 Card Hover Border Accent
264+ 
265+```jsx
266+// Add group class to parent and use:
267+className =
268+ "group border border-[var(--border-light)] hover:border-[var(--btn-primary-bg)]/50 transition-colors";
269+```
270+ 
271+### 6.3 Bullet/List Items
272+ 
273+```jsx
274+// Gold dot bullet
275+<li className="flex gap-2">
276+ <span className="mt-[6px] h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--btn-primary-bg)]" />
277+ <span className="text-[var(--text-secondary)]">{text}</span>
278+</li>
279+ 
280+// Gold triangle bullet
281+<li className="flex items-start gap-3">
282+ <span className="text-[var(--btn-primary-bg)] mt-1.5 shrink-0 text-[8px]">▶</span>
283+ <span className="text-[var(--text-secondary)]">{text}</span>
284+</li>
285+```
286+ 
287+### 6.4 Spec/Info Grid (2-column metadata)
288+ 
289+```jsx
290+<div
291+ className="grid grid-cols-2 gap-2 text-[10px] p-3 rounded-lg border
292+ border-[var(--border-light)]"
293+ style={{
294+ backgroundColor: isDark ? "rgba(255,255,255,0.02)" : "rgba(0,0,0,0.02)"
295+ }}
296+>
297+ {/* Items */}
298+</div>
299+```
300+ 
301+---
302+ 
303+## 7. BUTTON PATTERNS
304+ 
305+### 7.1 Primary Button (Gold fill)
306+ 
307+```jsx
308+<button
309+ className="px-6 py-3 rounded-lg text-xs font-black uppercase tracking-widest
310+ transition-all hover:brightness-110 active:scale-95"
311+ style={{backgroundColor: "var(--btn-primary-bg)", color: "#fff"}}
312+>
313+ {text}
314+</button>
315+```
316+ 
317+### 7.2 Secondary/Outline Button
318+ 
319+```jsx
320+<button
321+ className="py-3 rounded-lg border border-[var(--border-light)] text-xs
322+ font-bold transition-colors"
323+ style={{color: "var(--text-primary)", backgroundColor: "transparent"}}
324+>
325+ {text}
326+</button>
327+```
328+ 
329+### 7.3 Danger/Remove Button
330+ 
331+```jsx
332+<button
333+ className="py-3 rounded-lg text-xs font-bold uppercase tracking-widest"
334+ style={{
335+ color: "#f87171",
336+ backgroundColor: "rgba(239,68,68,0.08)",
337+ border: "1px solid rgba(239,68,68,0.25)"
338+ }}
339+>
340+ {text}
341+</button>
342+```
343+ 
344+### 7.4 Legacy `.main-button` (used by Button.jsx component)
345+ 
346+- Defined in `src/components/button/Button.scss`
347+- Gold background, white text, rounded, hover lift effect
348+- For NEW code, prefer primary button pattern above
349+ 
350+---
351+ 
352+## 8. SVG ICON PATTERN
353+ 
354+Use either approach consistently. For new code, prefer **inline SVG components** (portable, no external dependency).
355+ 
356+### 8.1 Inline SVGs (Recommended for new code)
357+ 
358+```jsx
359+const MyIcon = () => (
360+ <svg
361+ width="14"
362+ height="14"
363+ viewBox="0 0 24 24"
364+ fill="none"
365+ stroke="currentColor"
366+ strokeWidth="2"
367+ >
368+ <circle cx="12" cy="12" r="10" />
369+ <path d="M12 6v6l4 2" />
370+ </svg>
371+);
372+```
373+ 
374+Usage: `<MyIcon />` — color inherits from `currentColor`.
375+ 
376+### 8.2 FontAwesome (Used in SocialMedia, Contact, header)
377+ 
378+- Import: `import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"`
379+- Brand icons: `import { faGithub, faLinkedin } from "@fortawesome/free-brands-svg-icons"`
380+- Solid icons: `import { faEnvelope } from "@fortawesome/free-solid-svg-icons"`
381+- Brand colors defined as SCSS variables in `variables.scss` (e.g., `$linkedin: #0e76a8`)
382+ 
383+### 8.3 React Icons (Installed but unused — avoid if possible)
384+ 
385+- The `react-icons` package is installed but not currently used in any component.
386+- Prefer inline SVGs or FontAwesome instead.
387+ 
388+---
389+ 
390+## 9. FORM BLUEPRINT (WhatsApp/Email Actions)
391+ 
392+**Do NOT use `<form>` elements.** All interactions are direct links:
393+ 
394+```jsx
395+// WhatsApp
396+<a href={`https://wa.me/6281331487753?text=${encodeURIComponent(message)}`}
397+ target="_blank" rel="noopener noreferrer">
398+ Contact via WhatsApp
399+</a>
400+ 
401+// Email
402+<a href={`mailto:briliantfikri@gmail.com?subject=${subject}&body=${body}`}>
403+ Send Email
404+</a>
405+```
406+ 
407+For the pricing checkout message, construct the message template inside a `handleCheckout` function and call `window.open()`.
408+ 
409+---
410+ 
411+## 10. IMAGE HANDLING (Vite)
412+ 
413+- **Always import images statically** (Vite convention):
414+ ```jsx
415+ import myImage from "../../assets/images/myImage.png";
416+ // Usage: <img src={myImage} alt="..." />
417+ ```
418+- **Do NOT use `require()`** — that is a CRA/Webpack pattern. The Skills.jsx container is the only file still using `require()` and should be migrated.
419+- For images from external URLs, use `src={url}` directly (no import needed).
420+- Use `loading="lazy"` on below-the-fold images.
421+ 
422+---
423+ 
424+## 11. LANGUAGE / I18N PATTERN
425+ 
426+All user-facing text that supports bilingual display uses this pattern:
427+ 
428+1. In `portfolio.jsx`: define as `{ en: "...", id: "..." }` object.
429+2. In component: import `LanguageContext`, get `lang`, call `getTranslation(configField, lang)`.
430+3. The helper lives in `src/utils/translations.js`.
431+ 
432+```jsx
433+import LanguageContext from "../../contexts/LanguageContext";
434+import {getTranslation} from "../../utils/translations";
435+ 
436+const {lang} = useContext(LanguageContext);
437+const title = getTranslation(config.title, lang); // returns string
438+```
439+ 
440+**Rules:**
441+ 
442+- `getTranslation()` handles nested objects recursively (e.g., `workflow` steps).
443+- Arrays of bilingual objects are resolved element-by-element.
444+- Plain strings/numbers pass through unchanged.
445+ 
446+---
447+ 
448+## 12. MODAL / OVERLAY PATTERNS
449+ 
450+### 12.1 ImageLightbox (`src/components/imageLightbox/ImageLightbox.jsx`)
451+ 
452+```jsx
453+<ImageLightbox src={url} alt={text} onClose={() => setState(null)} />
454+```
455+ 
456+- Fixed overlay, dark backdrop, centered image, close on backdrop click or ✕ button.
457+- Always wrap with `{show && <ImageLightbox ... />}`.
458+ 
459+### 12.2 ProjectShowcase (`src/components/projectShowcase/ProjectShowcase.jsx`)
460+ 
461+```jsx
462+<ProjectShowcase
463+ title={string}
464+ description={string}
465+ media={[{type: "image" | "video", url, caption, thumbnail}]}
466+ externalUrl={
467+ string
468+ } /* Optional — shows "Visit Website" button in top-right header */
469+ onClose={fn}
470+/>
471+```
472+ 
473+- Full-screen dark overlay with media gallery.
474+- **"Visit Website"** button appears in top-right header ONLY when `externalUrl` is provided.
475+- Used by AchievementCard and BigProject containers.
476+ 
477+---
478+ 
479+## 13. THEME TOGGLE SYSTEM
480+ 
481+- Global context: `src/contexts/StyleContext.js` provides `{ isDark, changeTheme }`.
482+- Theme is persisted in `localStorage` via `useLocalStorage` hook.
483+- Toggle switch: `src/components/ToggleSwitch/ToggleSwitch.jsx` (sun/moon emoji).
484+- Theme class: `.light-mode` or `.dark-mode` on root `<div>` in `Main.jsx`.
485+- CSS variables defined in `src/variables.scss` respond to these classes.
486+ 
487+```jsx
488+import StyleContext from "../../contexts/StyleContext";
489+const { isDark } = useContext(StyleContext);
490+ 
491+// Use in conditional styling:
492+<div style={{ backgroundColor: isDark ? "rgba(0,0,0,0.4)" : "rgba(255,255,255,0.6)" }}>
493+```
494+ 
495+---
496+ 
497+## 14. LOTTIE ANIMATION PATTERN
498+ 
499+```jsx
500+import DisplayLottie from "../../components/displayLottie/DisplayLottie";
501+import animationData from "../../assets/lottie/myAnimation";
502+ 
503+{
504+ illustration.animated ? (
505+ <DisplayLottie animationData={animationData} />
506+ ) : (
507+ <img src={fallbackImage} alt="..." />
508+ );
509+}
510+```
511+ 
512+- All Lottie JSON files live in `src/assets/lottie/`.
513+- The `DisplayLottie` component wraps `lottie-react` with `Suspense` + `<Loading>` fallback.
514+ 
515+---
516+ 
517+## 15. FRAMER MOTION IMPORTS
518+ 
519+Always import only what's needed from `framer-motion`:
520+ 
521+```jsx
522+import {motion, AnimatePresence} from "framer-motion";
523+```
524+ 
525+- `motion.div`, `motion.button`, `motion.a`, `motion.span`, `motion.img`, `motion.h1`-`h6` supported.
526+- `AnimatePresence` for mounting/unmounting animations (modals, checkout bar).
527+ 
528+---
529+ 
530+## 16. KNOWN INCONSISTENCIES & MIGRATION NOTES
531+ 
532+These are NOT required to fix immediately, but be aware of them when touching related code:
533+ 
534+1. **`Skills.jsx` uses `require()` for static images** — should use `import` like all other containers (Vite pattern).
535+2. **`Button.jsx` does not spread `className`** — it wraps in `<div className={className}>` instead of applying directly to `<a>`. New button code should use the inline primary button pattern instead.
536+3. **`variables.scss` has legacy SCSS variables** that duplicate CSS custom properties (e.g., `$textColorDark` is `#ffffff` but already covered by `--text-primary` in dark mode). When refactoring, prefer CSS custom properties.
537+4. **`Greeting.scss` and `Skills.scss` still contain legacy classes** that are partially overridden by Tailwind. When editing, prefer moving styles to Tailwind and removing SCSS.
538+5. **`Header.scss` is partially refactored** — it has both legacy classes and a comment noting Tailwind removal was done. When editing, complete the migration.
539+6. **`SplashScreen.jsx` uses `.css` instead of `.scss`** — minor inconsistency.
540+7. **`ProjectShowcase.jsx` and `Pricing.jsx` both define duplicate inline SVG icons** (`Close`, `Image`) — consider extracting to `src/utils/icons.jsx` in the future.
541+ 
542+---
543+ 
544+## 17. FILE ORGANIZATION SUMMARY
545+ 
546+```
547+src/
548+├── portfolio.jsx # Single config file — all content data
549+├── App.jsx # Root component
550+├── Main.jsx # Container orchestrator, theme/language providers
551+├── variables.scss # Design tokens (CSS vars + SCSS vars)
552+├── contexts/
553+│ ├── StyleContext.js # Theme state (isDark, changeTheme)
554+│ └── LanguageContext.js # Language state (lang, changeLang)
555+├── hooks/
556+│ └── useLocalStorage.js # Persistent state hook
557+├── utils/
558+│ └── translations.js # getTranslation() helper
559+├── components/ # Reusable UI components
560+│ ├── header/Header.jsx
561+│ ├── button/Button.jsx
562+│ ├── footer/Footer.jsx
563+│ ├── socialMedia/SocialMedia.jsx
564+│ ├── ToggleSwitch/ToggleSwitch.jsx
565+│ ├── LanguageToggle/LanguageToggle.jsx
566+│ ├── displayLottie/DisplayLottie.jsx
567+│ ├── imageLightbox/ImageLightbox.jsx
568+│ ├── projectShowcase/ProjectShowcase.jsx
569+│ ├── achievementCard/AchievementCard.jsx
570+│ ├── educationCard/EducationCard.jsx
571+│ ├── experienceCard/ExperienceCard.jsx
572+│ ├── softwareSkills/SoftwareSkill.jsx
573+│ └── ... (other legacy components)
574+├── containers/ # Page sections (one per portfolio section)
575+│ ├── greeting/Greeting.jsx
576+│ ├── introVideo/IntroVideo.jsx
577+│ ├── skills/Skills.jsx
578+│ ├── education/Education.jsx
579+│ ├── workExperience/WorkExperience.jsx
580+│ ├── projects/Projects.jsx
581+│ ├── BigProjects/BigProject.jsx
582+│ ├── achievement/Achievement.jsx
583+│ ├── pricing/Pricing.jsx
584+│ ├── contact/Contact.jsx
585+│ ├── splashScreen/SplashScreen.jsx
586+│ └── ... (others)
587+└── assets/
588+ ├── images/ # Static images (imported in portfolio.jsx)
589+ ├── lottie/ # Lottie JSON animations
590+ └── fonts/ # Custom fonts
591+```
124592  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack