Alasan Mengapa Semua Landing Page Buatan AI Terlihat Sama dan Solusinya
Bagi pengembang solo yang menangani semuanya sendiri mulai dari perencanaan hingga penerapan, Cursor atau Claude Code terasa seperti penyelamat. Hanya dengan beberapa baris prompt, halaman web yang layak langsung jadi dalam waktu 10 menit.
Masalahnya ada setelah itu. Saat Anda menekan tombol deploy dan melihat layar, muncul perasaan déjà vu. Tombol gradasi ungu, font Inter, tata letak 3 kartu yang terpatri pas di tengah. Bau templat yang entah di mana pernah terlihat tercium kuat. Sekilas terlihat rapi, tetapi tidak berujung pada pembayaran atau pendaftaran pengguna. Orang-orang dengan sangat mahir dapat mengenali halaman yang tampak seperti buatan pabrik dan langsung menutup tab.
LLM bekerja berdasarkan rata-rata statistik. Jika Anda tidak memberikan aturan, mereka akan kembali ke preset bawaan Tailwind dan nilai bawaan Shadcn UI yang paling sering digunakan di web. Begitu Anda melontarkan prompt seperti "Buatlah landing page yang rapi dan elegan", AI akan memuntahkan nilai piksel inline yang tidak jelas seperti w-[320px], top-[117px], dan merusak tata letak.
M mencoba mengatasi masalah ini dengan modifikasi prompt saja akan membuat Anda kelelahan. Anda harus membangun sebuah pipa (pipeline) yang memaksa aturan merek setiap kali kode dihasilkan, dan secara fisik memblokir kode yang melanggar pada tahap commit.
Menulis File Aturan untuk Mengikat Tangan dan Kaki Agen AI
Cursor membaca .cursor/rules/*.mdc, Claude Code membaca CLAUDE.md, dan alat sumber terbuka membaca AGENTS.md. Jika Anda mendefinisikan token desain merek di file ini, AI tidak akan bisa menggunakan gaya sembarangan sejak tahap pembuatan kode.
Jika file aturan melebihi 500 baris, biaya konteks untuk membaca setiap sesi akan meningkat dan tingkat kepatuhan model juga akan menurun. Anda harus menulisnya dengan jelas sekitar 200–300 baris.
Buat .cursor/rules/design-system.mdc di root proyek dan masukkan konten berikut.
`yaml
description: Design System Rules and Custom Token Enforcement
globs: ["src/app//*.tsx", "src/components//.tsx", "src/styles/**/.css"]
alwaysApply: false
Brand Design System Constraints
Universal Rules
- MUST NOT use arbitrary Tailwind utility classes such as
bg-[#123456] or h-[117px].
- MUST use predefined semantic Design Tokens for colors, spacing, and typography.
- MUST run
pnpm lint:style to verify token compliance before completing tasks.
Design Token Reference Map
Color Tokens
- Surface Background:
var(--color-bg-primary) (Tailwind: bg-brand-primary)
- Surface Secondary:
var(--color-bg-secondary) (Tailwind: bg-brand-secondary)
- Text Main:
var(--color-text-main) (Tailwind: text-brand-main)
- Text Muted:
var(--color-text-muted) (Tailwind: text-brand-muted)
- Accent Primary:
var(--color-accent-default) (Tailwind: bg-brand-accent)
Spacing Scale (8pt Grid Standard)
var(--space-1): 0.25rem (4px)
var(--space-2): 0.5rem (8px)
var(--space-4): 1.0rem (16px)
var(--space-6): 1.5rem (24px)
var(--space-8): 2.0rem (32px)
Typography Rules
- Main Heading (H1): Class
text-brand-h1 -> Font: Inter, Weight: 700, Size: 2.5rem, Tracking: -0.02em
- Body Text: Class
text-brand-body -> Font: Inter, Weight: 400, Size: 1.0rem, Leading: 1.5
`
Dengan memasukkan aturan ini, frekuensi AI menyuntikkan nilai arbitrase tanda kurung siku atau kode Hex secara langsung akan berkurang secara drastis.
Memblokir Commit Gaya Hardcode dengan Stylelint dan Husky
Prompt saja tidak cukup. AI sering kali mengabaikan aturan dan diam-diam mencampur nilai piksel inline. Kita menggabungkan Stylelint dan Git Pre-commit hook untuk memblokir jalur masuk gaya yang di-hardcode ke dalam repositori.
Instal alat-alatnya dan inisialisasi Husky.
`bash
pnpm add -D husky lint-staged stylelint stylelint-declaration-strict-value
npx husky init
`
Buat stylelint.config.mjs di root proyek. Ini adalah pengaturan untuk memunculkan error build jika kode warna atau nilai piksel arbitrer dimasukkan.
`javascript
import type { Config } from "stylelint";
export default {
plugins: ["stylelint-declaration-strict-value"],
rules: {
"scale-unlimited/declaration-strict-value": [
["/color/", "font-size", "/margin/", "/padding/"],
{
ignoreVariables: false,
ignoreFunctions: false,
ignoreKeywords: {
"": ["transparent", "inherit", "currentColor", "auto", "0"]
},
message: "Design System Violation: Hardcoded value for '{property}' is forbidden. Use CSS Design Tokens instead."
}
]
}
} satisfies Config;
`
Tambahkan konfigurasi lint-staged ke package.json.
`json
{
"lint-staged": {
"*.{css,scss,tsx,jsx}": [
"stylelint --fix",
"eslint --max-warnings=0"
]
}
}
`
Daftarkan satu baris berikut ke dalam file .husky/pre-commit.
`bash
npx lint-staged
`
Sekarang setiap kali Anda mengetik git commit, kode yang di-staging akan dianalisis secara statis. Jika ada kode seperti margin: 17px yang dimasukkan sesuka hati oleh AI, proses commit itu sendiri akan gagal. Anda dapat menghemat sekitar 5 jam waktu per minggu yang biasa dihabiskan untuk perbaikan gaya.
Otomatisasi Uji Regresi Visual Menggunakan Playwright
Analisis statis hanya memeriksa aturan teks. Masalah seperti elemen yang saling tumpang tindih atau menu seluler yang rusak di layar ponsel baru bisa diketahui jika browser benar-benar dijalankan. Kita tidak mungkin memperkecil dan memperbesar viewport secara manual setiap saat, jadi kita mengotomatiskan perbandingan snapshot dengan Playwright.
Buat file playwright.config.ts.
`typescript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/visual',
snapshotPixelRatioTemplate: '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}',
expect: {
toHaveScreenshot: {
maxDiffPixelRatio: 0.01,
threshold: 0.2,
animations: 'disabled',
},
},
webServer: {
command: 'pnpm dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},
projects: [
{ name: 'Desktop Chrome', use: { ...devices['Desktop Chrome'] } },
{ name: 'Mobile Safari', use: { ...devices['iPhone 13'] } },
],
});
`
Tulis skrip pengujian (tests/visual/landing-page.spec.ts) yang memverifikasi 5 titik UI utama.
`typescript
import { test, expect } from '@playwright/test';
test.describe('Visual Regression Guardrails', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000');
await page.evaluate(() => document.fonts.ready);
});
test('TC1: Rendering Bagian Hero Desktop', async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
const heroSection = page.locator('section#hero');
await expect(heroSection).toBeVisible();
await expect(heroSection).toHaveScreenshot('hero-desktop.png', { maxDiffPixelRatio: 0.01 });
});
test('TC2: Rendering Viewport Menu Navigasi Seluler', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 812 });
const navBar = page.locator('header#main-nav');
await expect(navBar).toHaveScreenshot('nav-mobile.png');
});
test('TC3: Penyelarasan Grid Kartu Harga', async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 800 });
const pricingGrid = page.locator('div#pricing-cards');
await expect(pricingGrid).toHaveScreenshot('pricing-grid.png', {
mask: [page.locator('.dynamic-price-timestamp')]
});
});
test('TC4: Status Hover Tombol CTA Utama', async ({ page }) => {
const ctaButton = page.locator('button#primary-cta');
await ctaButton.hover();
await expect(ctaButton).toHaveScreenshot('cta-button-hover.png');
});
test('TC5: Tata Letak Modal Masuk', async ({ page }) => {
await page.click('button#open-login-modal');
const modalDialog = page.locator('div[role="dialog"]');
await expect(modalDialog).toBeVisible();
await expect(modalDialog).toHaveScreenshot('login-modal.png');
});
});
`
Daftarkan perintah ke package.json.
`json
{
"scripts": {
"test:visual": "playwright test",
"test:visual:update": "playwright test --update-snapshots"
}
}
`
Hanya dengan menjalankan satu baris pnpm test:visual di terminal, tata letak yang rusak pada viewport desktop dan seluler dapat dideteksi dalam waktu 1 menit. Pekerjaan melelahkan yang mengharuskan Anda menyesuaikan ukuran layar satu per satu dengan mata telanjang dapat disingkirkan.
Jika Anda memiliki struktur di mana input AI dikendalikan oleh file aturan, masuknya gaya yang salah dicegah oleh Git hook, dan kerusakan layar diperiksa menggunakan Playwright, Anda dapat mempertahankan konsistensi desain bahkan dalam proyek pengembangan solo.