Refactor components to use reusable PaperCard.
Introduced a new `PaperCard` component for consistent and reusable card styling across the app, replacing inline card styles. Added subtle animations and paper edge effects to enhance the visual design and provide a polished user experience. Updated existing components to integrate with the new `PaperCard` while maintaining backward compatibility.
This commit is contained in:
@@ -19,6 +19,16 @@ function cycleTag(tag) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PaperCard({ children, style }) {
|
||||||
|
return (
|
||||||
|
<div style={{ ...styles.paperCard, ...style }}>
|
||||||
|
<div style={styles.paperEdgeTop} aria-hidden="true" />
|
||||||
|
{children}
|
||||||
|
<div style={styles.paperEdgeBottom} aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function AdminPanel() {
|
function AdminPanel() {
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
|
|
||||||
@@ -223,7 +233,7 @@ export default function App() {
|
|||||||
document.head.appendChild(link);
|
document.head.appendChild(link);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ✅ Keyframes / Animationen
|
// ✅ Keyframes / Animationen (+ Paper Edge minimal)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (document.getElementById("hp-anim-style")) return;
|
if (document.getElementById("hp-anim-style")) return;
|
||||||
const style = document.createElement("style");
|
const style = document.createElement("style");
|
||||||
@@ -240,6 +250,13 @@ export default function App() {
|
|||||||
70% { opacity: .70; transform: translateY(1px) scale(1.01); filter: blur(17px); }
|
70% { opacity: .70; transform: translateY(1px) scale(1.01); filter: blur(17px); }
|
||||||
100% { opacity: .65; transform: translateY(0px) scale(1); filter: blur(16px); }
|
100% { opacity: .65; transform: translateY(0px) scale(1); filter: blur(16px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Paper edge: super subtle, keeps it "alive" without causing scroll lag */
|
||||||
|
@keyframes paperEdgeDrift {
|
||||||
|
0% { opacity: .92; transform: translateX(0px); }
|
||||||
|
50% { opacity: .98; transform: translateX(0.6px); }
|
||||||
|
100% { opacity: .92; transform: translateX(0px); }
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -282,7 +299,6 @@ export default function App() {
|
|||||||
if (root) {
|
if (root) {
|
||||||
root.style.minHeight = "100dvh";
|
root.style.minHeight = "100dvh";
|
||||||
root.style.background = "transparent";
|
root.style.background = "transparent";
|
||||||
// extra: verhindert manchmal "white flash" bei overscroll
|
|
||||||
root.style.overflowX = "hidden";
|
root.style.overflowX = "hidden";
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
@@ -397,7 +413,7 @@ export default function App() {
|
|||||||
return (
|
return (
|
||||||
<div style={styles.loginPage}>
|
<div style={styles.loginPage}>
|
||||||
<div style={styles.candleGlowLayer} aria-hidden="true" />
|
<div style={styles.candleGlowLayer} aria-hidden="true" />
|
||||||
<div style={styles.loginCard}>
|
<PaperCard style={styles.loginCard}>
|
||||||
<div style={styles.loginTitle}>Zauber-Detektiv Notizbogen</div>
|
<div style={styles.loginTitle}>Zauber-Detektiv Notizbogen</div>
|
||||||
|
|
||||||
<div style={styles.loginSubtitle}>
|
<div style={styles.loginSubtitle}>
|
||||||
@@ -447,7 +463,7 @@ export default function App() {
|
|||||||
Deine Notizen bleiben privat – jeder Spieler sieht nur seinen eigenen
|
Deine Notizen bleiben privat – jeder Spieler sieht nur seinen eigenen
|
||||||
Zettel.
|
Zettel.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PaperCard>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -455,7 +471,11 @@ export default function App() {
|
|||||||
// ===== Main App =====
|
// ===== Main App =====
|
||||||
const sections = sheet
|
const sections = sheet
|
||||||
? [
|
? [
|
||||||
{ key: "suspect", title: "VERDÄCHTIGE PERSON", entries: sheet.suspect || [] },
|
{
|
||||||
|
key: "suspect",
|
||||||
|
title: "VERDÄCHTIGE PERSON",
|
||||||
|
entries: sheet.suspect || [],
|
||||||
|
},
|
||||||
{ key: "item", title: "GEGENSTAND", entries: sheet.item || [] },
|
{ key: "item", title: "GEGENSTAND", entries: sheet.item || [] },
|
||||||
{ key: "location", title: "ORT", entries: sheet.location || [] },
|
{ key: "location", title: "ORT", entries: sheet.location || [] },
|
||||||
]
|
]
|
||||||
@@ -514,7 +534,7 @@ export default function App() {
|
|||||||
|
|
||||||
{/* Spiel + Hilfe */}
|
{/* Spiel + Hilfe */}
|
||||||
<div style={{ marginTop: 14 }}>
|
<div style={{ marginTop: 14 }}>
|
||||||
<div style={styles.card}>
|
<PaperCard>
|
||||||
<div style={styles.sectionHeader}>Spiel</div>
|
<div style={styles.sectionHeader}>Spiel</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -545,7 +565,7 @@ export default function App() {
|
|||||||
Hilfe
|
Hilfe
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PaperCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hilfe Modal */}
|
{/* Hilfe Modal */}
|
||||||
@@ -633,9 +653,12 @@ export default function App() {
|
|||||||
|
|
||||||
<div style={styles.helpDivider} />
|
<div style={styles.helpDivider} />
|
||||||
|
|
||||||
<div style={styles.helpSectionTitle}>2) i / m / s Button (Notiz)</div>
|
<div style={styles.helpSectionTitle}>
|
||||||
|
2) i / m / s Button (Notiz)
|
||||||
|
</div>
|
||||||
<div style={styles.helpText}>
|
<div style={styles.helpText}>
|
||||||
Rechts pro Zeile gibt es einen Button, der durch diese Werte rotiert:
|
Rechts pro Zeile gibt es einen Button, der durch diese Werte
|
||||||
|
rotiert:
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={styles.helpList}>
|
<div style={styles.helpList}>
|
||||||
@@ -679,7 +702,7 @@ export default function App() {
|
|||||||
{/* Sheet */}
|
{/* Sheet */}
|
||||||
<div style={{ marginTop: 14, display: "grid", gap: 14 }}>
|
<div style={{ marginTop: 14, display: "grid", gap: 14 }}>
|
||||||
{sections.map((sec) => (
|
{sections.map((sec) => (
|
||||||
<div key={sec.key} style={styles.card}>
|
<PaperCard key={sec.key}>
|
||||||
<div style={styles.sectionHeader}>{sec.title}</div>
|
<div style={styles.sectionHeader}>{sec.title}</div>
|
||||||
|
|
||||||
<div style={{ display: "grid" }}>
|
<div style={{ display: "grid" }}>
|
||||||
@@ -689,7 +712,10 @@ export default function App() {
|
|||||||
style={{
|
style={{
|
||||||
...styles.row,
|
...styles.row,
|
||||||
background: getRowBg(e.status),
|
background: getRowBg(e.status),
|
||||||
animation: pulseId === e.entry_id ? "rowPulse 220ms ease-out" : "none",
|
animation:
|
||||||
|
pulseId === e.entry_id
|
||||||
|
? "rowPulse 220ms ease-out"
|
||||||
|
: "none",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -724,7 +750,7 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PaperCard>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -776,6 +802,18 @@ const styles = {
|
|||||||
backdropFilter: "blur(4px)",
|
backdropFilter: "blur(4px)",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// === Paper card base (replaces `card` visually) ===
|
||||||
|
paperCard: {
|
||||||
|
position: "relative",
|
||||||
|
borderRadius: 16,
|
||||||
|
overflow: "hidden",
|
||||||
|
border: "1px solid rgba(0,0,0,0.18)",
|
||||||
|
background: "rgba(255,255,255,0.35)",
|
||||||
|
boxShadow: "0 10px 24px rgba(0,0,0,0.12)",
|
||||||
|
transition: "transform 180ms ease, box-shadow 180ms ease",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fallback (kept, in case you still use it somewhere)
|
||||||
card: {
|
card: {
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
@@ -785,6 +823,82 @@ const styles = {
|
|||||||
transition: "transform 180ms ease, box-shadow 180ms ease",
|
transition: "transform 180ms ease, box-shadow 180ms ease",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Paper edges (top + bottom) — mask-based, lightweight, no heavy blur/filters
|
||||||
|
paperEdgeTop: {
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
height: 18,
|
||||||
|
pointerEvents: "none",
|
||||||
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.62), rgba(255,255,255,0.0))",
|
||||||
|
opacity: 0.95,
|
||||||
|
|
||||||
|
WebkitMaskImage:
|
||||||
|
"radial-gradient(10px 8px at 6% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 14% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 24% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 36% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 49% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 62% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 74% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 86% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 94% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"linear-gradient(#000, #000)",
|
||||||
|
maskImage:
|
||||||
|
"radial-gradient(10px 8px at 6% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 14% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 24% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 36% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 49% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 62% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 74% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 86% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 94% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"linear-gradient(#000, #000)",
|
||||||
|
|
||||||
|
filter: "drop-shadow(0 1px 0 rgba(0,0,0,0.14))",
|
||||||
|
animation: "paperEdgeDrift 8s ease-in-out infinite",
|
||||||
|
},
|
||||||
|
|
||||||
|
paperEdgeBottom: {
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
height: 20,
|
||||||
|
pointerEvents: "none",
|
||||||
|
background:
|
||||||
|
"linear-gradient(0deg, rgba(255,255,255,0.62), rgba(255,255,255,0.0))",
|
||||||
|
opacity: 0.95,
|
||||||
|
transform: "scaleY(-1)",
|
||||||
|
WebkitMaskImage:
|
||||||
|
"radial-gradient(10px 8px at 6% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 14% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 24% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 36% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 49% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 62% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 74% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 86% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 94% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"linear-gradient(#000, #000)",
|
||||||
|
maskImage:
|
||||||
|
"radial-gradient(10px 8px at 6% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 14% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 24% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 36% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 49% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(12px 9px at 62% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(9px 7px at 74% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(13px 10px at 86% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"radial-gradient(10px 8px at 94% 100%, #000 98%, transparent 102%)," +
|
||||||
|
"linear-gradient(#000, #000)",
|
||||||
|
filter: "drop-shadow(0 1px 0 rgba(0,0,0,0.14))",
|
||||||
|
animation: "paperEdgeDrift 8s ease-in-out infinite",
|
||||||
|
},
|
||||||
|
|
||||||
sectionHeader: {
|
sectionHeader: {
|
||||||
padding: "10px 12px",
|
padding: "10px 12px",
|
||||||
fontWeight: 1000,
|
fontWeight: 1000,
|
||||||
@@ -824,7 +938,8 @@ const styles = {
|
|||||||
fontWeight: 1000,
|
fontWeight: 1000,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
border: "1px solid rgba(0,0,0,0.25)",
|
border: "1px solid rgba(0,0,0,0.25)",
|
||||||
background: "linear-gradient(180deg, rgba(255,255,255,0.55), rgba(0,0,0,0.06))",
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.55), rgba(0,0,0,0.06))",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -832,7 +947,8 @@ const styles = {
|
|||||||
padding: "10px 12px",
|
padding: "10px 12px",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
border: "1px solid rgba(0,0,0,0.25)",
|
border: "1px solid rgba(0,0,0,0.25)",
|
||||||
background: "linear-gradient(180deg, rgba(255,255,255,0.75), rgba(0,0,0,0.06))",
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.75), rgba(0,0,0,0.06))",
|
||||||
fontWeight: 1000,
|
fontWeight: 1000,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
@@ -862,7 +978,8 @@ const styles = {
|
|||||||
padding: "10px 12px",
|
padding: "10px 12px",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
border: "1px solid rgba(0,0,0,0.25)",
|
border: "1px solid rgba(0,0,0,0.25)",
|
||||||
background: "linear-gradient(180deg, rgba(255,255,255,0.75), rgba(0,0,0,0.05))",
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.75), rgba(0,0,0,0.05))",
|
||||||
fontWeight: 900,
|
fontWeight: 900,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transition: "transform 140ms ease, box-shadow 140ms ease",
|
transition: "transform 140ms ease, box-shadow 140ms ease",
|
||||||
@@ -907,7 +1024,8 @@ const styles = {
|
|||||||
maxWidth: 560,
|
maxWidth: 560,
|
||||||
borderRadius: 18,
|
borderRadius: 18,
|
||||||
border: "1px solid rgba(0,0,0,0.25)",
|
border: "1px solid rgba(0,0,0,0.25)",
|
||||||
background: "linear-gradient(180deg, rgba(255,255,255,0.72), rgba(255,255,255,0.42))",
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.72), rgba(255,255,255,0.42))",
|
||||||
boxShadow: "0 18px 50px rgba(0,0,0,0.35)",
|
boxShadow: "0 18px 50px rgba(0,0,0,0.35)",
|
||||||
padding: 14,
|
padding: 14,
|
||||||
backdropFilter: "blur(6px)",
|
backdropFilter: "blur(6px)",
|
||||||
@@ -924,7 +1042,8 @@ const styles = {
|
|||||||
height: 38,
|
height: 38,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
border: "1px solid rgba(0,0,0,0.25)",
|
border: "1px solid rgba(0,0,0,0.25)",
|
||||||
background: "linear-gradient(180deg, rgba(255,255,255,0.85), rgba(0,0,0,0.06))",
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.85), rgba(0,0,0,0.06))",
|
||||||
fontWeight: 1000,
|
fontWeight: 1000,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
lineHeight: "38px",
|
lineHeight: "38px",
|
||||||
@@ -1014,13 +1133,10 @@ const styles = {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
maxWidth: 420,
|
maxWidth: 420,
|
||||||
padding: 26,
|
padding: 26,
|
||||||
borderRadius: 22,
|
borderRadius: 22, // paperCard uses 16; this overrides for login
|
||||||
position: "relative",
|
position: "relative",
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
border: "1px solid rgba(0,0,0,0.25)",
|
// border/background/boxShadow handled by PaperCard base
|
||||||
background: "linear-gradient(180deg, rgba(255,255,255,0.72), rgba(255,255,255,0.40))",
|
|
||||||
boxShadow: "0 18px 55px rgba(0,0,0,0.35)",
|
|
||||||
backdropFilter: "blur(8px)",
|
|
||||||
animation: "popIn 240ms ease-out",
|
animation: "popIn 240ms ease-out",
|
||||||
},
|
},
|
||||||
loginTitle: {
|
loginTitle: {
|
||||||
@@ -1106,7 +1222,8 @@ const styles = {
|
|||||||
borderRadius: "0 12px 12px 0",
|
borderRadius: "0 12px 12px 0",
|
||||||
border: "1px solid rgba(0,0,0,0.25)",
|
border: "1px solid rgba(0,0,0,0.25)",
|
||||||
borderLeft: "none",
|
borderLeft: "none",
|
||||||
background: "linear-gradient(180deg, rgba(255,255,255,0.85), rgba(0,0,0,0.06))",
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.85), rgba(0,0,0,0.06))",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
fontWeight: 900,
|
fontWeight: 900,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user