Cline rules
.clinerules/ui-standards.mdCline rules
Quality
76/100
Scores the file, not the repository.Length
2,188 words
42 headings · 24 code blocksRepository
0
— · pushed 51 days agoLast changed
3 days ago
First indexed 3 days ago.1# Liant Portfolio - UI Standards & Component Patterns23> **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.45---67## 1. COLOR SYSTEM89### 1.1 Brand Gold (Primary Accent)1011| 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 |1617**Critical:** The gold in `tailwind.config.js` must always match `variables.scss` -> `$brand-gold`. Currently both use `#a1902e`.1819### 1.2 Theme Tokens (CSS Custom Properties)2021Always use these in JSX via Tailwind arbitrary values — never hardcode colors:2223```jsx24// LIGHT MODE (.light-mode) — Neutral cool grey, no warm/pink tint25--bg-body: #eaeaea26--bg-card: #f5f5f527--bg-header: #f5f5f528--text-primary: #1a1a2e29--text-secondary: #3a3a4e30--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)3233// DARK MODE (.dark-mode, [data-theme='dark'])34--bg-body: #0a0a0a35--bg-card: #1f1f1f36--bg-header: #161b2237--text-primary: #ffffff38--text-secondary: #b0b0b039--border-light: rgba(255, 255, 255, 0.1)40--shadow-card: 0 4px 6px rgba(255, 255, 255, 0.05)41```4243### 1.3 Tailwind Arbitrary Value Pattern4445```jsx46bg-[var(--bg-body)] // Section backgrounds47bg-[var(--bg-card)] // Card backgrounds48text-[var(--text-primary)] // Main heading text49text-[var(--text-secondary)] // Subtitle, metadata, description text50border-[var(--border-light)] // Subtle borders and dividers51text-[var(--btn-primary-bg)] // Brand accent text52bg-[var(--btn-primary-bg)] // Primary button fill53hover:text-[var(--btn-primary-bg)] // Link/button hover accent54hover:border-[var(--btn-primary-bg)] // Card hover border accent55```5657---5859## 2. SECTION LAYOUT PATTERN6061Every section follows this exact structure in JSX:6263```jsx64import {motion} from "framer-motion";65import LanguageContext from "../../contexts/LanguageContext";66import {getTranslation} from "../../utils/translations";6768export default function MySection() {69 const {lang} = useContext(LanguageContext);70 const {isDark} = useContext(StyleContext); // if theme needed7172 if (!config.display) return null;7374 return (75 <section76 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.div83 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 <p93 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>99100 {/* 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```109110### Section IDs (used by header nav anchors)111112| 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 |123124---125126## 3. ANIMATION CONVENTIONS127128### 3.1 Scroll-Triggered Reveal (Framer Motion)129130Use `whileInView` for all scroll-triggered animations (not `animate`, which runs on mount):131132```jsx133// Single element134<motion.div135 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>;142143// Staggered children144const 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};155156<motion.div157 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```169170### 3.2 Hover Animations171172```jsx173// Card lift on hover174<motion.div whileHover={{y: -5}}>...</motion.div>;175176// Button/link with CSS transition177className = "transition-all duration-300 hover:-translate-y-1";178```179180### 3.3 Floating/Looping Animation (for decorative elements)181182```jsx183<motion.span184 animate={{y: [0, -6, 0]}}185 transition={{duration: 3, repeat: Infinity, ease: "easeInOut"}}186>187 🚀188</motion.span>189```190191---192193## 4. TYPOGRAPHY CONVENTIONS194195| 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 |203204---205206## 5. SECTION HEADER PATTERN (STANDARDIZED)207208All sections must use the shared `SectionHeader` component located at `src/components/sectionHeader/SectionHeader.jsx`. This ensures visual harmony and consistency across the portfolio.209210### Usage211212```jsx213import SectionHeader from "../../components/sectionHeader/SectionHeader";214215<SectionHeader216 title="Section Title" // Required217 subtitle="Optional subtitle" // Optional218 emoji="🚀" // Optional emoji above title219 align="center" // "center" (default) | "left"220/>;221```222223### Component API224225| 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 |231232### Rendered Output233234- **Title**: `text-3xl md:text-5xl font-bold text-[var(--text-primary)]`235- **Emoji** (optional): `text-4xl md:text-5xl` above title236- **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)]`238239### Example Usage by Section240241| 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 |251252---253254## 6. CARD STANDARDS255256### 6.1 Card Container257258```jsx259<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```262263### 6.2 Card Hover Border Accent264265```jsx266// 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```270271### 6.3 Bullet/List Items272273```jsx274// Gold dot bullet275<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>279280// Gold triangle bullet281<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```286287### 6.4 Spec/Info Grid (2-column metadata)288289```jsx290<div291 className="grid grid-cols-2 gap-2 text-[10px] p-3 rounded-lg border292 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```300301---302303## 7. BUTTON PATTERNS304305### 7.1 Primary Button (Gold fill)306307```jsx308<button309 className="px-6 py-3 rounded-lg text-xs font-black uppercase tracking-widest310 transition-all hover:brightness-110 active:scale-95"311 style={{backgroundColor: "var(--btn-primary-bg)", color: "#fff"}}312>313 {text}314</button>315```316317### 7.2 Secondary/Outline Button318319```jsx320<button321 className="py-3 rounded-lg border border-[var(--border-light)] text-xs322 font-bold transition-colors"323 style={{color: "var(--text-primary)", backgroundColor: "transparent"}}324>325 {text}326</button>327```328329### 7.3 Danger/Remove Button330331```jsx332<button333 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```343344### 7.4 Legacy `.main-button` (used by Button.jsx component)345346- Defined in `src/components/button/Button.scss`347- Gold background, white text, rounded, hover lift effect348- For NEW code, prefer primary button pattern above349350---351352## 8. SVG ICON PATTERN353354Use either approach consistently. For new code, prefer **inline SVG components** (portable, no external dependency).355356### 8.1 Inline SVGs (Recommended for new code)357358```jsx359const MyIcon = () => (360 <svg361 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```373374Usage: `<MyIcon />` — color inherits from `currentColor`.375376### 8.2 FontAwesome (Used in SocialMedia, Contact, header)377378- 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`)382383### 8.3 React Icons (Installed but unused — avoid if possible)384385- The `react-icons` package is installed but not currently used in any component.386- Prefer inline SVGs or FontAwesome instead.387388---389390## 9. FORM BLUEPRINT (WhatsApp/Email Actions)391392**Do NOT use `<form>` elements.** All interactions are direct links:393394```jsx395// WhatsApp396<a href={`https://wa.me/6281331487753?text=${encodeURIComponent(message)}`}397 target="_blank" rel="noopener noreferrer">398 Contact via WhatsApp399</a>400401// Email402<a href={`mailto:briliantfikri@gmail.com?subject=${subject}&body=${body}`}>403 Send Email404</a>405```406407For the pricing checkout message, construct the message template inside a `handleCheckout` function and call `window.open()`.408409---410411## 10. IMAGE HANDLING (Vite)412413- **Always import images statically** (Vite convention):414```jsx415 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.421422---423424## 11. LANGUAGE / I18N PATTERN425426All user-facing text that supports bilingual display uses this pattern:4274281. 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`.431432```jsx433import LanguageContext from "../../contexts/LanguageContext";434import {getTranslation} from "../../utils/translations";435436const {lang} = useContext(LanguageContext);437const title = getTranslation(config.title, lang); // returns string438```439440**Rules:**441442- `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.445446---447448## 12. MODAL / OVERLAY PATTERNS449450### 12.1 ImageLightbox (`src/components/imageLightbox/ImageLightbox.jsx`)451452```jsx453<ImageLightbox src={url} alt={text} onClose={() => setState(null)} />454```455456- Fixed overlay, dark backdrop, centered image, close on backdrop click or ✕ button.457- Always wrap with `{show && <ImageLightbox ... />}`.458459### 12.2 ProjectShowcase (`src/components/projectShowcase/ProjectShowcase.jsx`)460461```jsx462<ProjectShowcase463 title={string}464 description={string}465 media={[{type: "image" | "video", url, caption, thumbnail}]}466 externalUrl={467 string468 } /* Optional — shows "Visit Website" button in top-right header */469 onClose={fn}470/>471```472473- 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.476477---478479## 13. THEME TOGGLE SYSTEM480481- 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.486487```jsx488import StyleContext from "../../contexts/StyleContext";489const { isDark } = useContext(StyleContext);490491// Use in conditional styling:492<div style={{ backgroundColor: isDark ? "rgba(0,0,0,0.4)" : "rgba(255,255,255,0.6)" }}>493```494495---496497## 14. LOTTIE ANIMATION PATTERN498499```jsx500import DisplayLottie from "../../components/displayLottie/DisplayLottie";501import animationData from "../../assets/lottie/myAnimation";502503{504 illustration.animated ? (505 <DisplayLottie animationData={animationData} />506 ) : (507 <img src={fallbackImage} alt="..." />508 );509}510```511512- All Lottie JSON files live in `src/assets/lottie/`.513- The `DisplayLottie` component wraps `lottie-react` with `Suspense` + `<Loading>` fallback.514515---516517## 15. FRAMER MOTION IMPORTS518519Always import only what's needed from `framer-motion`:520521```jsx522import {motion, AnimatePresence} from "framer-motion";523```524525- `motion.div`, `motion.button`, `motion.a`, `motion.span`, `motion.img`, `motion.h1`-`h6` supported.526- `AnimatePresence` for mounting/unmounting animations (modals, checkout bar).527528---529530## 16. KNOWN INCONSISTENCIES & MIGRATION NOTES531532These are NOT required to fix immediately, but be aware of them when touching related code:5335341. **`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.541542---543544## 17. FILE ORGANIZATION SUMMARY545546```547src/548├── portfolio.jsx # Single config file — all content data549├── App.jsx # Root component550├── Main.jsx # Container orchestrator, theme/language providers551├── 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 hook557├── utils/558│ └── translations.js # getTranslation() helper559├── components/ # Reusable UI components560│ ├── header/Header.jsx561│ ├── button/Button.jsx562│ ├── footer/Footer.jsx563│ ├── socialMedia/SocialMedia.jsx564│ ├── ToggleSwitch/ToggleSwitch.jsx565│ ├── LanguageToggle/LanguageToggle.jsx566│ ├── displayLottie/DisplayLottie.jsx567│ ├── imageLightbox/ImageLightbox.jsx568│ ├── projectShowcase/ProjectShowcase.jsx569│ ├── achievementCard/AchievementCard.jsx570│ ├── educationCard/EducationCard.jsx571│ ├── experienceCard/ExperienceCard.jsx572│ ├── softwareSkills/SoftwareSkill.jsx573│ └── ... (other legacy components)574├── containers/ # Page sections (one per portfolio section)575│ ├── greeting/Greeting.jsx576│ ├── introVideo/IntroVideo.jsx577│ ├── skills/Skills.jsx578│ ├── education/Education.jsx579│ ├── workExperience/WorkExperience.jsx580│ ├── projects/Projects.jsx581│ ├── BigProjects/BigProject.jsx582│ ├── achievement/Achievement.jsx583│ ├── pricing/Pricing.jsx584│ ├── contact/Contact.jsx585│ ├── splashScreen/SplashScreen.jsx586│ └── ... (others)587└── assets/588 ├── images/ # Static images (imported in portfolio.jsx)589 ├── lottie/ # Lottie JSON animations590 └── fonts/ # Custom fonts591```592
Also in BryaanF/LiantPortfolio
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| BryaanF/LiantPortfolio.clinerules/project-guidelines.md · 0 | Cline rules | buildstylearchgit+2 | 96/100 | 3 days ago | |
| BryaanF/LiantPortfolio.github/copilot-instructions.md · 0 | Copilot instructions | setupbuildstylearch+4 | 89/100 | 3 days ago |
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| bashdeban/fastmind.clinerules/.project-consistency-keeper2.md · 5 | Cline rules | setupbuildtestlint-format+11 | 100/100 | 3 days ago | |
| JCodesMore/ai-website-cloner-template.clinerules · 31k | Cline rules | buildlint-formatstylearch+3 | 97/100 | 2 days ago | |
| BryaanF/LiantPortfolio.clinerules/project-guidelines.md · 0 | Cline rules | buildstylearchgit+2 | 96/100 | 3 days ago | |
| u9401066/zotero-keeper.clinerules/50-pubmed-project.md · 6 | Cline rules | testlint-formatstylearch+1 | 94/100 | 3 days ago | |
| u9401066/zotero-keepervscode-extension/resources/repo-assets/pubmed-search-mcp/.clinerules/50-pubmed-project.md · 6 | Cline rules | testlint-formatstylearch+1 | 94/100 | 3 days ago | |
| prabhakar267/paper-games.clinerules/git-commit-guidelines.md · 0 | Cline rules | lint-formatstylearchgit+3 | 93/100 | 2 days ago | |
| VaillerTeeter/HoshimiNest.clinerules/project-identity.md · 1 | Cline rules | setuparchtypesdo-not | 93/100 | yesterday | |
| HerringtonDarkholme/megarepo.clinerules/02-development.md · 17 | Cline rules | setupbuildteststyle+3 | 92/100 | 3 days ago |
