Uygulama · Kurulum rehberleri
Her ortamda aynı tasarım.
fkdev UI yalnızca CSS sınıfları ve küçük bir JS dosyasıdır; bu yüzden PHP’den React’e her yerde aynı şekilde çalışır.
01Saf HTML#
Hiçbir araç gerekmez. İlk sayfa şablonunu al, her sayfayı kendi klasörüne index.html olarak koy, sunucuya yükle.
02PHP: düzen ve parçalar#
PHP’de ortak header ve footer’ı bir kez yazıp her sayfada include et. Bu klasör yapısı paylaşımlı hostingde (cPanel, Plesk) doğrudan çalışır.
site/
├── index.php
├── iletisim/index.php
├── api/iletisim.php
├── inc/
│ ├── config.php
│ ├── header.php
│ └── footer.php
└── .htaccess<?php
declare(strict_types=1);
session_start();
if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(32));
function e(?string $s): string { return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8'); }
function csrf_field(): string { return '<input type="hidden" name="csrf" value="' . e($_SESSION['csrf']) . '">'; }
function flash(string $msg, string $type = 'success'): void { $_SESSION['flash'] = [$msg, $type]; }<?php require __DIR__ . '/config.php'; ?>
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= e($title ?? 'Sitem') ?></title>
<meta name="description" content="<?= e($description ?? '') ?>">
<link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500&family=Geist:wght@300..700&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/fkdev/fkdev-ui.css">
<script src="/assets/fkdev/fkdev-ui.js" defer></script>
</head>
<body class="fk-body fk-atmosphere">
<header class="fk-navbar">
<div class="fk-navbar-in fk-glass">
<a class="fk-brand" href="/"><b>Site <i>Adı</i></b></a>
<nav class="fk-tabsbar">
<span class="fk-tabsbar-ind"></span>
<a href="/" <?= ($page ?? '') === 'home' ? 'aria-current="page"' : '' ?>>Ana sayfa</a>
<a href="/iletisim/" <?= ($page ?? '') === 'iletisim' ? 'aria-current="page"' : '' ?>>İletişim</a>
</nav>
<div class="fk-navbar-tools"><button class="fk-btn fk-btn-icon" data-fk-theme-toggle aria-label="Tema">◐</button></div>
</div>
</header>
<main class="fk-wrap"> </main>
<footer class="fk-footer">
<div class="fk-footer-card">
<a class="fk-footer-brand" href="/">Site <i>Adı</i></a>
<p class="fk-footer-bottom">© <?= date('Y') ?></p>
</div>
</footer>
<?php if (!empty($_SESSION['flash'])): [$m, $t] = $_SESSION['flash']; unset($_SESSION['flash']); ?>
<script>addEventListener('DOMContentLoaded', () => FK.toast(<?= json_encode($m) ?>, <?= json_encode($t) ?>));</script>
<?php endif; ?>
</body>
</html><?php
$title = 'İletişim — Sitem';
$description = 'Bize ulaşın.';
$page = 'iletisim';
require __DIR__ . '/../inc/header.php';
?>
<section class="fk-center" style="padding:120px 0 40px">
<h1 class="fk-h1">Konuşa<em>lım.</em></h1>
</section>
<form class="fk-form fk-card" method="post" action="/api/iletisim.php" data-fk-validate style="max-width:560px;margin:0 auto">
<?= csrf_field() ?>
<div class="fk-field">
<label class="fk-label" for="ad">Ad</label>
<input class="fk-input" id="ad" name="ad" required minlength="2">
<span class="fk-error"></span>
</div>
<div class="fk-field">
<label class="fk-label" for="mail">E-posta</label>
<input class="fk-input" id="mail" name="mail" type="email" required>
<span class="fk-error"></span>
</div>
<div class="fk-field">
<label class="fk-label" for="mesaj">Mesaj</label>
<textarea class="fk-textarea" id="mesaj" name="mesaj" required minlength="10"></textarea>
<span class="fk-error"></span>
</div>
<button class="fk-btn fk-btn-primary" type="submit">Gönder</button>
</form>
<?php require __DIR__ . '/../inc/footer.php'; ?>03PHP: form işleme#
<?php
require __DIR__ . '/../inc/config.php';
require __DIR__ . '/../config/db.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
http_response_code(403);
exit('Geçersiz istek.');
}
$ad = trim($_POST['ad'] ?? '');
$mail = trim($_POST['mail'] ?? '');
$mesaj = trim($_POST['mesaj'] ?? '');
if (mb_strlen($ad) < 2 || !filter_var($mail, FILTER_VALIDATE_EMAIL) || mb_strlen($mesaj) < 10) {
flash('Lütfen alanları kontrol edin.', 'danger');
header('Location: /iletisim/');
exit;
}
$stmt = db()->prepare('INSERT INTO messages (channel, name, email, body, ip) VALUES (?, ?, ?, ?, ?)');
$stmt->execute(['contact', $ad, $mail, $mesaj, $_SERVER['REMOTE_ADDR'] ?? '']);
flash('Mesajın iletildi, teşekkürler!');
header('Location: /iletisim/');<?php
$f = $_FILES['dosya'] ?? null;
if (!$f || $f['error'] !== UPLOAD_ERR_OK || $f['size'] > 150 * 1024 * 1024) exit('Geçersiz dosya.');
$ext = strtolower(pathinfo($f['name'], PATHINFO_EXTENSION));
$izin = ['pdf', 'zip', 'png', 'jpg', 'jpeg', 'docx', 'xlsx'];
if (!in_array($ext, $izin, true)) exit('Bu dosya türü kabul edilmiyor.');
$hedef = __DIR__ . '/../storage/' . bin2hex(random_bytes(16)) . '.' . $ext;
move_uploaded_file($f['tmp_name'], $hedef);storage klasörünü dışarı kapatYüklenen dosyaların durduğu klasör web’den doğrudan erişilebilir olmamalı. Mümkünse public klasörün dışına koy ya da içine Require all denied içeren bir .htaccess yerleştir.
04Node.js / Express#
npm init -y
npm install expressconst express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'public'), { extensions: ['html'] }));
app.use(express.json());
app.post('/api/iletisim', (req, res) => {
const { ad, mail, mesaj } = req.body || {};
if (!ad || !mail || !mesaj) return res.status(400).json({ ok: false, error: 'Eksik alan' });
res.json({ ok: true });
});
app.use((req, res) => res.status(404).sendFile(path.join(__dirname, 'public', '404.html')));
if (require.main === module) app.listen(process.env.PORT || 3000);
module.exports = app;05Vercel#
Statik bir fkdev UI sitesi Vercel’e ek ayar olmadan yüklenir. Express uygulaması için:
{
"version": 2,
"builds": [{ "src": "index.js", "use": "@vercel/node" }],
"routes": [{ "src": "/(.*)", "dest": "/index.js" }]
}Vercel sınırlarıVercel fonksiyonlarında istek gövdesi 4,5 MB ile sınırlıdır; büyük dosya yüklemeleri doğrudan bir depolamaya (ör. Vercel Blob) yapılmalı. Bellekte tutulan oturumlar da kopyalar arasında paylaşılmaz; oturumu veritabanında ya da imzalı çerezde tut.
06React ve Next.js#
CSS’i bir kez global olarak içe aktar, class yerine className kullan. Sayfa değiştikçe yeni öğeleri başlatmak için FK.init() çağır.
import Script from 'next/script';
import './fkdev-ui.css';
export default function RootLayout({ children }) {
return (
<html lang="tr">
<body className="fk-body fk-atmosphere">
{children}
<Script src="/fkdev-ui.js" strategy="afterInteractive" />
</body>
</html>
);
}import { useEffect, useRef } from 'react';
export function Kart({ baslik, children }) {
const ref = useRef(null);
useEffect(() => { window.FK && window.FK.init(ref.current); }, []);
return (
<article ref={ref} className="fk-card fk-reveal">
<h3 className="fk-card-title">{baslik}</h3>
<p>{children}</p>
</article>
);
}07Vue#
import { onMounted, ref } from 'vue';
const kok = ref(null);
onMounted(() => window.FK && window.FK.init(kok.value));08Laravel (Blade)#
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'Sitem')</title>
<link rel="stylesheet" href="{{ asset('fkdev/fkdev-ui.css') }}">
<script src="{{ asset('fkdev/fkdev-ui.js') }}" defer></script>
</head>
<body class="fk-body fk-atmosphere">
@include('partials.navbar')
<main class="fk-wrap">@yield('content')</main>
@if (session('ok'))
<script>addEventListener('DOMContentLoaded', () => FK.toast(@json(session('ok')), 'success'));</script>
@endif
</body>
</html>09WordPress#
<?php
add_action('wp_enqueue_scripts', function () {
wp_enqueue_style('fkdev-fonts', 'https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500&family=Geist:wght@300..700&family=Instrument+Serif:ital@0;1&display=swap', [], null);
wp_enqueue_style('fkdev-ui', 'https://ui.fkdev.sbs/v2.4.1/fkdev-ui.min.css', [], '2.4.1');
wp_enqueue_script('fkdev-ui', 'https://ui.fkdev.sbs/v2.4.1/fkdev-ui.min.js', [], '2.4.1', ['strategy' => 'defer']);
});
add_filter('body_class', fn($c) => array_merge($c, ['fk-body', 'fk-atmosphere']));10Tailwind ve Bootstrap ile birlikte#
Sınıflar fk- önekli olduğu için çakışmaz. Tailwind’in renklerini fkdev değişkenlerine bağlayarak tek bir tasarım dili kullanabilirsin:
export default {
theme: {
extend: {
colors: { ink: 'var(--fk-ink)', soft: 'var(--fk-soft)', surface: 'var(--fk-surface)' },
fontFamily: { serif: ['Instrument Serif', 'serif'], sans: ['Geist', 'sans-serif'] }
}
}
};