neumorphism – skill Thiết kế & UI cho agent AI
Hướng dẫn triển khai Neumorphism (Soft UI) cho Web và App. Kích hoạt khi người dùng muốn bóng đổ mềm, hiệu ứng nổi khối và mô phỏng nguồn sáng.
Cài skill neumorphism bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/neumorphism-d9888.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /neumorphism – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/neumorphism-d9888.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $neumorphism – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/neumorphism-d9888.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /neumorphism – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/neumorphism-d9888.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /neumorphism – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
Neumorphism (Soft UI)
> "Elements extruded from the background material itself, shaped by a singular, persistent light source."
When to Use
Use this sub-style when the user's request matches the aesthetic described above. This is a child reference of the design-it skill and is not meant to be triggered directly.
Core Principles
1. Unified Surface Color: The background and the elements MUST share the exact same base color.
2. Dual Shadows: Elements are shaped by two shadows: a light shadow (highlight) on the side facing the light source, and a dark shadow on the opposite side.
3. Accessible Boundaries: Shadows establish the style, but add a visible border or other indicator when needed to identify controls and focus.
Visual DNA
- Colors: Works best with mid-tone neutrals. Desert Mirage, Earth-Grounded Elegance, or Sophisticated Neutral are perfect. Avoid pure white or pure black (shadows/highlights won't show).
- Typography: Soft, rounded sans-serifs (e.g.,
Nunito,Quicksand). - Shapes: Pill shapes, rounded rectangles. Sharp corners break the illusion of extruded material.
Web Implementation
- The magic is entirely in
box-shadowmanipulating light and dark variants of the base color. - CSS Example:
:root {
--base-color: #E6E2DD; /* From Sophisticated Neutral */
--highlight: #ffffff;
--shadow: #c4c0bc;
}
body {
background-color: var(--base-color);
}
.neu-element {
background-color: var(--base-color);
border-radius: 20px;
/* Top-left highlight, Bottom-right shadow */
box-shadow: 9px 9px 18px var(--shadow),
-9px -9px 18px var(--highlight);
padding: 32px;
}
.neu-pressed {
/* Inset shadows for pressed/active state */
border-radius: 20px;
background: var(--base-color);
box-shadow: inset 9px 9px 18px var(--shadow),
inset -9px -9px 18px var(--highlight);
}App Implementation
SwiftUI
struct NeuCard: View {
let baseColor = Color(red: 0.90, green: 0.89, blue: 0.87) // #E6E2DD
var body: some View {
VStack(spacing: 24) {
Text("Neumorphic Card")
.font(.system(size: 20, weight: .semibold, design: .rounded))
Text("Extruded from the surface itself.")
.font(.system(size: 15, design: .rounded))
.foregroundColor(.secondary)
}
.padding(32)
.background(baseColor)
.cornerRadius(20)
// Light shadow (top-left)
.shadow(color: Color.white.opacity(0.7), radius: 10, x: -8, y: -8)
// Dark shadow (bottom-right)
.shadow(color: Color.black.opacity(0.15), radius: 10, x: 8, y: 8)
}
}
// Pressed / inset neumorphic button
struct NeuButton: View {
@State private var isPressed = false
let baseColor = Color(red: 0.90, green: 0.89, blue: 0.87)
var body: some View {
Button(action: {}) {
Text("Press Me")
.font(.system(size: 16, weight: .semibold, design: .rounded))
.foregroundColor(.primary)
.padding(.horizontal, 32)
.padding(.vertical, 16)
}
.background(
Group {
if isPressed {
// Inset effect using inner shadow (ZStack trick)
RoundedRectangle(cornerRadius: 16)
.fill(baseColor)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(baseColor, lineWidth: 4)
.shadow(color: Color.black.opacity(0.2), radius: 4, x: 4, y: 4)
.clipShape(RoundedRectangle(cornerRadius: 16))
)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(baseColor, lineWidth: 4)
.shadow(color: Color.white.opacity(0.7), radius: 4, x: -4, y: -4)
.clipShape(RoundedRectangle(cornerRadius: 16))
)
} else {
RoundedRectangle(cornerRadius: 16)
.fill(baseColor)
.shadow(color: Color.white.opacity(0.7), radius: 10, x: -8, y: -8)
.shadow(color: Color.black.opacity(0.15), radius: 10, x: 8, y: 8)
}
}
)
.buttonStyle(.plain)
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { _ in isPressed = true }
.onEnded { _ in isPressed = false }
)
}
}- The key trick: two
.shadow()modifiers — one white (top-left), one dark (bottom-right). - Recent SwiftUI supports ShapeStyle.shadow(.inner(...)); the overlay example is a fallback for older targets, not the only implementation.
- The view's background color MUST match its parent's background exactly.
Flutter
class NeuCard extends StatelessWidget {
final Color baseColor = const Color(0xFFE6E2DD);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
color: baseColor,
borderRadius: BorderRadius.circular(20),
boxShadow: [
// Dark shadow (bottom-right)
BoxShadow(
color: Colors.black.withOpacity(0.15),
offset: const Offset(8, 8),
blurRadius: 16,
),
// Light shadow (top-left)
BoxShadow(
color: Colors.white.withOpacity(0.7),
offset: const Offset(-8, -8),
blurRadius: 16,
),
],
),
child: Column(
children: [
const Text('Neumorphic Card',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600)),
const SizedBox(height: 16),
Text('Extruded from the surface itself.',
style: TextStyle(fontSize: 15, color: Colors.black54)),
],
),
);
}
}- Inner shadows (for pressed state) are NOT natively supported in Flutter's
BoxShadow. - Use the
flutter_inset_box_shadowpackage OR fake it with a layeredStack: place aContainerwith a dark gradient overlay on top and a light gradient below. - Set the
Scaffoldbackground to the SAMEbaseColorso elements look extruded.
React Native
const NeuCard = () => (
<View style={{
padding: 32,
backgroundColor: '#E6E2DD',
borderRadius: 20,
// Light shadow (top-left) — iOS only supports one shadow
shadowColor: '#FFFFFF',
shadowOffset: { width: -8, height: -8 },
shadowOpacity: 0.7,
shadowRadius: 10,
// Android — use elevation for basic shadow
elevation: 8,
}}>
<Text style={{ fontSize: 20, fontWeight: '600' }}>Neumorphic Card</Text>
<Text style={{ fontSize: 15, color: '#555', marginTop: 16 }}>
Extruded from the surface itself.
</Text>
</View>
);- Legacy API limitation: The shadowColor/shadowOffset family provides one shadow per view. New Architecture boxShadow supports multiple/inset shadows on supported OS versions; verify the target version before adding a dependency.
- Workaround: Use
react-native-shadow-2orreact-native-neomorph-shadowswhich provide multi-shadow support. - Alternative: Wrap two nested
Views — the outer one has the dark shadow, the inner one has the light shadow. - Inner shadows for pressed states require SVG-based solutions or pre-rendered images.
Jetpack Compose
@Composable
fun NeuCard() {
val baseColor = Color(0xFFE6E2DD)
Box(
modifier = Modifier
.padding(24.dp)
.shadow(
elevation = 8.dp,
shape = RoundedCornerShape(20.dp),
ambientColor = Color.Black.copy(alpha = 0.15f),
spotColor = Color.Black.copy(alpha = 0.15f),
)
.background(baseColor, RoundedCornerShape(20.dp))
.padding(32.dp)
) {
Column {
Text("Neumorphic Card",
fontSize = 20.sp, fontWeight = FontWeight.SemiBold)
Spacer(Modifier.height(16.dp))
Text("Extruded from the surface itself.",
fontSize = 15.sp, color = Color(0xFF555555))
}
}
}- Compose limitation: Like React Native, Compose
Modifier.shadow()only supports a single directional shadow. - For true dual-shadow neumorphism, use a custom
Modifier.drawBehind { }withdrawIntoCanvasto paint two separate shadow paths (one light, one dark). - The
neumorphic-composelibrary provides a pre-builtModifier.neumorphic()that handles both shadows. - Set the
Scaffoldbackground to the samebaseColor— this is non-negotiable.
Do's and Don'ts
- DO: Use inset shadows for "active" states (like pressed buttons o
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- nosql-expert – Hướng dẫn chuyên sâu cho cơ sở dữ liệu NoSQL phân tán (Cassandra, DynamoDB). Tập trung…
- not-a-vibe-coder – Biến các prompt mơ hồ thành 8 tệp lập kế hoạch có cấu trúc cho dự án mới hoàn toàn…
- nsfw-ai-spicyapi – Tạo ảnh người lớn (18+), clip image-to-video và chỉnh sửa ảnh qua SpicyAPI, với báo giá…
- obsess – Chế độ cầu toàn cho một mục tiêu: thử nghiệm có kiểm tra trên chi tiết UI hoặc hot path…
- offers – Khi người dùng muốn thiết kế, xây dựng hoặc cải thiện một offer (ưu đãi) — thứ họ thực…
- og-tag-generator – Công cụ tạo OG Tag. Dùng khi làm việc với URL trang, tiêu đề, mô tả và URL ảnh công khai…
- onboarding-psychologist – Thiết kế trải nghiệm lần đầu sử dụng và onboarding mang lại thành công sớm, giảm ma sát…
- organization-design – Sổ đăng ký cơ cấu tổ chức: phòng ban, loại, đơn vị cha, người đứng đầu, địa điểm, được…