feat(tema): açık / koyu / sistem teması
All checks were successful
Deploy / deploy (push) Successful in 6m23s
All checks were successful
Deploy / deploy (push) Successful in 6m23s
- TemaSaglayici (next-themes) + alt bilgide TemaSecici - globals.css: .dark bloğunda palet token'ları çevrilir; bileşenlere dark: sınıfı yazılmaz - yüzeyler bg-white → bg-card, koyu haplar text-slate-50, SVG'lerde sabit renk yerine değişken - DESIGN.md §2.3 + tasarim-denetim kuralları (yuzey-beyaz, koyu-hap-beyaz-yazi, dark-sinif), AGENTS.md karanlık mod bölümü - docs: koyu tema denetimi raporu Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -71,7 +71,7 @@ export function AramaFunnelKarti({ onSec }: { onSec: () => void }) {
|
||||
return (
|
||||
<section
|
||||
aria-label="Tercih listeni kur"
|
||||
className="relative isolate mt-5 overflow-hidden rounded-2xl border border-blue-400/20 bg-slate-950 px-4 py-5 text-white sm:px-5"
|
||||
className="kt-koyu-yuzey relative isolate mt-5 overflow-hidden rounded-2xl border border-blue-400/20 bg-slate-950 px-4 py-5 text-white sm:px-5"
|
||||
>
|
||||
<PikselZemin seed={61} />
|
||||
<div className="grid items-center gap-4 sm:grid-cols-[minmax(0,1fr)_auto] sm:gap-5">
|
||||
|
||||
@@ -47,7 +47,7 @@ export function CtaBandi({
|
||||
className={cn(
|
||||
"relative isolate overflow-hidden rounded-2xl",
|
||||
ton === "beyaz"
|
||||
? "border border-slate-200 bg-white text-slate-900"
|
||||
? "border border-slate-200 bg-card text-slate-900"
|
||||
: "bg-primary text-white",
|
||||
buyuk ? "px-5 py-8 text-center sm:p-12" : "px-5 py-4 text-left sm:py-5",
|
||||
className,
|
||||
|
||||
@@ -52,8 +52,8 @@ const BASIS =
|
||||
|
||||
// Yarı saydam malzeme; şeffaflık azaltılsın diyen kullanıcıya opak yüzey.
|
||||
const MALZEME =
|
||||
"border border-white/60 bg-white/80 shadow-2xl shadow-slate-900/20 backdrop-blur-2xl backdrop-saturate-150 " +
|
||||
"[@media(prefers-reduced-transparency:reduce)]:bg-white [@media(prefers-reduced-transparency:reduce)]:backdrop-blur-none";
|
||||
"border border-white/60 bg-card/80 shadow-2xl shadow-slate-900/20 backdrop-blur-2xl backdrop-saturate-150 " +
|
||||
"[@media(prefers-reduced-transparency:reduce)]:bg-card [@media(prefers-reduced-transparency:reduce)]:backdrop-blur-none";
|
||||
|
||||
export function DevPanel() {
|
||||
const [acik, setAcik] = useState(false);
|
||||
@@ -139,7 +139,7 @@ export function DevPanel() {
|
||||
onClick={paneliAc}
|
||||
title="Dev süperadmin paneli"
|
||||
style={{ borderRadius: 18 }}
|
||||
className={`fixed bottom-20 left-4 z-50 inline-flex h-9 cursor-pointer items-center gap-1.5 px-3 text-xs font-bold tracking-wide text-white ${MALZEME} !bg-slate-900/85 print:hidden ${BASIS} hover:!bg-slate-800/85`}
|
||||
className={`fixed bottom-20 left-4 z-50 inline-flex h-9 cursor-pointer items-center gap-1.5 px-3 text-xs font-bold tracking-wide text-slate-50 ${MALZEME} !bg-slate-900/85 print:hidden ${BASIS} hover:!bg-slate-800/85`}
|
||||
>
|
||||
<Wrench className="size-3.5 text-violet-300" aria-hidden />
|
||||
DEV
|
||||
@@ -186,7 +186,7 @@ export function DevPanel() {
|
||||
Oturum
|
||||
</p>
|
||||
{g ? (
|
||||
<div className="mt-2 rounded-xl border border-slate-900/5 bg-white/60 px-3 py-2 text-xs">
|
||||
<div className="mt-2 rounded-xl border border-slate-900/5 bg-card/60 px-3 py-2 text-xs">
|
||||
<p className="font-semibold text-slate-800">{g.email}</p>
|
||||
<p className="mt-0.5 tabular-nums text-slate-500">
|
||||
{g.kredi} kredi · {g.hasPaket ? "paketli" : "paketsiz"} ·{" "}
|
||||
@@ -218,7 +218,7 @@ export function DevPanel() {
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="deneme@ornek.dev"
|
||||
className="h-9 min-w-0 flex-1 rounded-lg border border-slate-900/10 bg-white/70 px-2.5 text-xs outline-none transition-colors duration-150 focus-visible:border-violet-400"
|
||||
className="h-9 min-w-0 flex-1 rounded-lg border border-slate-900/10 bg-card/70 px-2.5 text-xs outline-none transition-colors duration-150 focus-visible:border-violet-400"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@@ -272,7 +272,7 @@ export function DevPanel() {
|
||||
type="button"
|
||||
disabled={mesgul || !g}
|
||||
onClick={() => calistir(s.fn)}
|
||||
className={`cursor-pointer rounded-xl border border-slate-900/10 bg-white/60 px-2.5 py-2 text-left text-xs hover:border-violet-300 hover:bg-violet-50/80 disabled:cursor-default disabled:opacity-40 ${BASIS}`}
|
||||
className={`cursor-pointer rounded-xl border border-slate-900/10 bg-card/60 px-2.5 py-2 text-left text-xs hover:border-violet-300 hover:bg-violet-50/80 disabled:cursor-default disabled:opacity-40 ${BASIS}`}
|
||||
>
|
||||
<span className="block font-semibold text-slate-800">
|
||||
{s.ad}
|
||||
@@ -325,7 +325,7 @@ export function DevPanel() {
|
||||
<a
|
||||
key={s.href}
|
||||
href={s.href}
|
||||
className={`cursor-pointer rounded-full border border-slate-900/10 bg-white/60 px-2.5 py-1 text-[11px] text-slate-700 hover:border-violet-300 hover:bg-violet-50/80 ${BASIS}`}
|
||||
className={`cursor-pointer rounded-full border border-slate-900/10 bg-card/60 px-2.5 py-1 text-[11px] text-slate-700 hover:border-violet-300 hover:bg-violet-50/80 ${BASIS}`}
|
||||
>
|
||||
{s.ad}
|
||||
</a>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function OzetKart({
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col rounded-2xl border border-slate-200 bg-white p-4 sm:p-5 ${className ?? ""}`}
|
||||
className={`flex flex-col rounded-2xl border border-slate-200 bg-card p-4 sm:p-5 ${className ?? ""}`}
|
||||
>
|
||||
<dt className="order-2 mt-2 text-base font-semibold text-slate-900">
|
||||
{etiket}
|
||||
|
||||
@@ -38,7 +38,7 @@ export function PikselCtaYuzeyi({
|
||||
onFocusCapture={(e) => setTamamla(hedefMi(e.target))}
|
||||
onBlurCapture={() => setTamamla(false)}
|
||||
className={cn(
|
||||
"relative isolate overflow-hidden rounded-3xl border border-blue-400/20 bg-slate-950 text-white shadow-xl shadow-slate-900/10",
|
||||
"kt-koyu-yuzey relative isolate overflow-hidden rounded-3xl border border-blue-400/20 bg-slate-950 text-white shadow-xl shadow-slate-900/10",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -481,7 +481,7 @@ export function SectionEyebrow({
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3.5 py-1.5 text-xs font-semibold text-primary",
|
||||
"inline-flex items-center gap-2 rounded-full border border-slate-200 bg-card px-3.5 py-1.5 text-xs font-semibold text-primary",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -2,6 +2,7 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Amiri_Quran } from "next/font/google";
|
||||
import { PixelMark } from "@/components/pixel-decor";
|
||||
import { TemaSecici } from "@/components/tema-secici";
|
||||
import { getAllBolumler } from "@/lib/katalog";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -199,6 +200,7 @@ export function SiteFooter() {
|
||||
İletişim
|
||||
</Link>
|
||||
</nav>
|
||||
<TemaSecici className="mt-6" />
|
||||
{/* iyzico işyeri sözleşmesi gereği: "iyzico ile Öde" + kart logoları
|
||||
sitede görünür olmalı. Resmî logo bandı (iyzico logo paketi). */}
|
||||
<Image
|
||||
@@ -206,7 +208,7 @@ export function SiteFooter() {
|
||||
alt="iyzico ile Öde — Mastercard, Visa, American Express ve Troy ile güvenli ödeme"
|
||||
width={429}
|
||||
height={32}
|
||||
className="mx-auto mt-6 h-6 w-auto"
|
||||
className="kt-acik-logo mx-auto mt-6 h-6 w-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
43
src/components/tema-saglayici.tsx
Normal file
43
src/components/tema-saglayici.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { ThemeProvider, useTheme } from "next-themes";
|
||||
|
||||
// Gövde zemini (bg-slate-50) — açık ve koyu temadaki karşılığı
|
||||
const ZEMIN = { light: "#f8fafc", dark: "#080e19" };
|
||||
|
||||
/**
|
||||
* Mobil adres çubuğu rengi: layout'taki `themeColor` sistemi izler; kullanıcı
|
||||
* sitede sistemden farklı bir tema seçtiyse meta etiketleri ona eşitlenir.
|
||||
*/
|
||||
function AdresCubuguRengi() {
|
||||
const { resolvedTheme } = useTheme();
|
||||
useEffect(() => {
|
||||
const renk = resolvedTheme === "dark" ? ZEMIN.dark : ZEMIN.light;
|
||||
for (const meta of document.querySelectorAll('meta[name="theme-color"]')) {
|
||||
meta.setAttribute("content", renk);
|
||||
}
|
||||
}, [resolvedTheme]);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tema (DESIGN.md §2.3): <html>'e `dark` sınıfı yazılır, renkler
|
||||
* globals.css'teki `.dark` token eşlemesinden gelir. Varsayılan açık tema;
|
||||
* kullanıcı footer'daki TemaSecici ile koyu ya da "sistem"e geçer, seçim
|
||||
* tarayıcıda saklanır.
|
||||
*/
|
||||
export function TemaSaglayici({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="light"
|
||||
enableSystem
|
||||
storageKey="kt-tema"
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<AdresCubuguRengi />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
61
src/components/tema-secici.tsx
Normal file
61
src/components/tema-secici.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Monitor, Moon, Sun } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const SECENEKLER = [
|
||||
{ deger: "light", etiket: "Açık", ikon: Sun },
|
||||
{ deger: "dark", etiket: "Koyu", ikon: Moon },
|
||||
{ deger: "system", etiket: "Sistem", ikon: Monitor },
|
||||
] as const;
|
||||
|
||||
const aboneOlma = () => () => {};
|
||||
|
||||
/**
|
||||
* Tema seçici: açık / koyu / sistem (DESIGN.md §2.3). Seçili tema yalnızca
|
||||
* tarayıcıda bilinir; sunucu HTML'inde hiçbir seçenek işaretli gelmez,
|
||||
* hydration sonrası işaretlenir (yerleşim oynamaz).
|
||||
*/
|
||||
export function TemaSecici({ className }: { className?: string }) {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const tarayicida = useSyncExternalStore(
|
||||
aboneOlma,
|
||||
() => true,
|
||||
() => false,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
role="radiogroup"
|
||||
aria-label="Tema"
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded-full border border-slate-200 bg-card p-1",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{SECENEKLER.map(({ deger, etiket, ikon: Ikon }) => {
|
||||
const secili = tarayicida && theme === deger;
|
||||
return (
|
||||
<button
|
||||
key={deger}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={secili}
|
||||
onClick={() => setTheme(deger)}
|
||||
className={cn(
|
||||
"inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-full px-3 text-sm font-semibold transition-[background-color,color,transform] duration-150 outline-none focus-visible:ring-3 focus-visible:ring-ring/50 active:scale-[0.97]",
|
||||
secili
|
||||
? "bg-slate-900 text-slate-50"
|
||||
: "text-slate-600 hover:text-orange-500",
|
||||
)}
|
||||
>
|
||||
<Ikon className="size-4" aria-hidden />
|
||||
{etiket}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -17,7 +17,7 @@ function Avatar({
|
||||
data-slot="avatar"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
|
||||
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Slot } from "radix-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
||||
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -13,11 +13,11 @@ const badgeVariants = cva(
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
||||
destructive:
|
||||
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
||||
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 [a]:hover:bg-destructive/20",
|
||||
outline:
|
||||
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
||||
ghost:
|
||||
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
||||
"hover:bg-muted hover:text-muted-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,19 +5,19 @@ import { Slot } from "radix-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"group/button inline-flex shrink-0 items-center justify-center rounded-full border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group/button inline-flex shrink-0 items-center justify-center rounded-full border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
outline:
|
||||
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
||||
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
||||
ghost:
|
||||
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
||||
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
|
||||
destructive:
|
||||
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
||||
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useTheme } from "next-themes"
|
||||
import React, { useEffect, useState } from "react"
|
||||
|
||||
export type CodeBlockProps = {
|
||||
@@ -33,11 +34,15 @@ export type CodeBlockCodeProps = {
|
||||
function CodeBlockCode({
|
||||
code,
|
||||
language = "tsx",
|
||||
theme = "github-light",
|
||||
theme: temaProp,
|
||||
className,
|
||||
...props
|
||||
}: CodeBlockCodeProps) {
|
||||
const [highlightedHtml, setHighlightedHtml] = useState<string | null>(null)
|
||||
// Tema verilmediyse sitenin temasını izler (DESIGN.md §2.3)
|
||||
const { resolvedTheme } = useTheme()
|
||||
const theme =
|
||||
temaProp ?? (resolvedTheme === "dark" ? "github-dark" : "github-light")
|
||||
|
||||
useEffect(() => {
|
||||
async function highlight() {
|
||||
|
||||
@@ -13,7 +13,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"h-12 w-full min-w-0 rounded-full border border-slate-200 bg-white px-5 text-base text-slate-900 transition-colors duration-150 outline-none placeholder:text-slate-400 hover:border-slate-300 focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-50 aria-invalid:border-red-500 aria-invalid:ring-3 aria-invalid:ring-red-500/20",
|
||||
"h-12 w-full min-w-0 rounded-full border border-slate-200 bg-card px-5 text-base text-slate-900 transition-colors duration-150 outline-none placeholder:text-slate-400 hover:border-slate-300 focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-50 aria-invalid:border-red-500 aria-invalid:ring-3 aria-invalid:ring-red-500/20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -5,7 +5,7 @@ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
|
||||
<kbd
|
||||
data-slot="kbd"
|
||||
className={cn(
|
||||
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
|
||||
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background [&_svg:not([class*='size-'])]:size-3",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -44,7 +44,7 @@ function SelectTrigger({
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"flex w-fit cursor-pointer items-center justify-between gap-2 rounded-full border border-slate-200 bg-white py-2 pr-4 pl-5 text-base text-slate-900 whitespace-nowrap transition-colors duration-150 outline-none select-none hover:border-slate-300 focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-red-500 aria-invalid:ring-3 aria-invalid:ring-red-500/20 data-placeholder:text-slate-400 data-[size=default]:h-12 data-[size=sm]:h-9 data-[size=sm]:pr-3 data-[size=sm]:pl-4 data-[size=sm]:text-sm *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"flex w-fit cursor-pointer items-center justify-between gap-2 rounded-full border border-slate-200 bg-card py-2 pr-4 pl-5 text-base text-slate-900 whitespace-nowrap transition-colors duration-150 outline-none select-none hover:border-slate-300 focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-red-500 aria-invalid:ring-3 aria-invalid:ring-red-500/20 data-placeholder:text-slate-400 data-[size=default]:h-12 data-[size=sm]:h-9 data-[size=sm]:pr-3 data-[size=sm]:pl-4 data-[size=sm]:text-sm *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -69,7 +69,7 @@ function SelectContent({
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
data-align-trigger={position === "item-aligned"}
|
||||
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-2xl border border-slate-200 bg-white text-slate-900 shadow-lg duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
|
||||
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-2xl border border-slate-200 bg-card text-slate-900 shadow-lg duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
|
||||
position={position}
|
||||
align={align}
|
||||
{...props}
|
||||
@@ -148,7 +148,7 @@ function SelectScrollUpButton({
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
data-slot="select-scroll-up-button"
|
||||
className={cn(
|
||||
"z-10 flex cursor-default items-center justify-center bg-white py-1 text-slate-400 [&_svg:not([class*='size-'])]:size-4",
|
||||
"z-10 flex cursor-default items-center justify-center bg-card py-1 text-slate-400 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -167,7 +167,7 @@ function SelectScrollDownButton({
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
data-slot="select-scroll-down-button"
|
||||
className={cn(
|
||||
"z-10 flex cursor-default items-center justify-center bg-white py-1 text-slate-400 [&_svg:not([class*='size-'])]:size-4",
|
||||
"z-10 flex cursor-default items-center justify-center bg-card py-1 text-slate-400 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
"use client"
|
||||
|
||||
import { useTheme } from "next-themes"
|
||||
import { Toaster as Sonner, type ToasterProps } from "sonner"
|
||||
import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon } from "lucide-react"
|
||||
|
||||
/**
|
||||
* Bildirimler (DESIGN.md §4.16). Tema "light"e sabit: üründe karanlık mod
|
||||
* yok (§1.2); sistem koyu temadayken sonner açıklamayı beyaza yakın griye
|
||||
* çevirip beyaz zeminde okunmaz hâle getiriyordu (Bilal, 24 Eyl 2026:
|
||||
* "toast açıklaması okunmuyor"). Metin ve buton renkleri globals.css'te.
|
||||
* Bildirimler (DESIGN.md §4.16). Tema sitenin temasını izler (sistemin
|
||||
* değil): sonner kendi "system" algısıyla açık sitede koyu toast metni
|
||||
* basıp açıklamayı okunmaz ediyordu (Bilal, 24 Eyl 2026: "toast açıklaması
|
||||
* okunmuyor"). Metin ve buton renkleri globals.css'te.
|
||||
*/
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
return (
|
||||
<Sonner
|
||||
theme="light"
|
||||
theme={resolvedTheme === "dark" ? "dark" : "light"}
|
||||
className="toaster group"
|
||||
icons={{
|
||||
success: (
|
||||
|
||||
@@ -33,7 +33,7 @@ const tabsListVariants = cva(
|
||||
line: "gap-1 bg-transparent",
|
||||
// Nav'daki "Giriş yap" pili ile aynı dil: beyaz kapsül içinde koyu
|
||||
// aktif hap (bkz. user-nav.tsx). Kayan gösterge tüketicide çizilir.
|
||||
pill: "gap-1 rounded-full border border-slate-200 bg-white p-1 group-data-horizontal/tabs:h-auto",
|
||||
pill: "gap-1 rounded-full border border-slate-200 bg-card p-1 group-data-horizontal/tabs:h-auto",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -66,12 +66,12 @@ function TabsTrigger({
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
||||
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
|
||||
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
||||
"data-active:bg-background data-active:text-foreground",
|
||||
// Pill varyantı: arka planı tüketicideki kayan gösterge verir, trigger
|
||||
// şeffaf kalır; aktif metin koyu hap üstünde beyaza döner.
|
||||
"group-data-[variant=pill]/tabs-list:h-auto group-data-[variant=pill]/tabs-list:rounded-full group-data-[variant=pill]/tabs-list:data-active:bg-transparent group-data-[variant=pill]/tabs-list:data-active:text-white",
|
||||
// şeffaf kalır; aktif metin koyu hap üstünde açık tona döner (koyu temada hap açık, metin koyu).
|
||||
"group-data-[variant=pill]/tabs-list:h-auto group-data-[variant=pill]/tabs-list:rounded-full group-data-[variant=pill]/tabs-list:data-active:bg-transparent group-data-[variant=pill]/tabs-list:data-active:text-slate-50",
|
||||
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -12,7 +12,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"flex field-sizing-content min-h-24 w-full rounded-2xl border border-slate-200 bg-white px-5 py-3 text-base text-slate-900 transition-colors duration-150 outline-none placeholder:text-slate-400 hover:border-slate-300 focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-50 aria-invalid:border-red-500 aria-invalid:ring-3 aria-invalid:ring-red-500/20",
|
||||
"flex field-sizing-content min-h-24 w-full rounded-2xl border border-slate-200 bg-card px-5 py-3 text-base text-slate-900 transition-colors duration-150 outline-none placeholder:text-slate-400 hover:border-slate-300 focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-50 aria-invalid:border-red-500 aria-invalid:ring-3 aria-invalid:ring-red-500/20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in New Issue
Block a user