Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Web and App implementation guide for Dashboard Design. Trigger when user wants analytics-focused layouts, data visualization, and modular overview screens.
.claude/skills/lingxling-dashboard-design/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 42% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 123% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 136% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 115% | 0% |
> "Data at a glance. Organized, scannable, and highly functional."
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.
Inter, Roboto Mono for numbers).cssbody { background-color: #F8F9FA; color: #212529; font-family: 'Inter', sans-serif; margin: 0; } .dashboard-layout { display: grid; grid-template-columns: 250px 1fr; grid-template-rows: 70px 1fr; height: 100vh; } .sidebar { grid-row: 1 / 3; background-color: #ffffff; border-right: 1px solid #e9ecef; padding: 20px; } .header { background-color: #ffffff; border-bottom: 1px solid #e9ecef; padding: 0 30px; display: flex; align-items: center; } .main-content { padding: 30px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; overflow-y: auto; } /* KPI Card */ .kpi-card { background: #fff; border-radius: 8px; padding: 24px; border: 1px solid #e9ecef; box-shadow: 0 2px 4px rgba(0,0,0,0.02); } .kpi-title { font-size: 0.9rem; color: #6c757d; } .kpi-value { font-size: 2rem; font-weight: 700; margin-top: 8px; } .kpi-trend.positive { color: #28a745; }
swiftstruct DashboardView: View { // For iPad/Mac, NavigationSplitView is ideal. // For iPhone, we use a scrolling VGrid. let columns = [ GridItem(.adaptive(minimum: 150), spacing: 16) ] var body: some View { NavigationView { ScrollView { LazyVGrid(columns: columns, spacing: 16) { KPICard(title: "Revenue", value: "$45,231", trend: "+12.5%", isPositive: true) KPICard(title: "Active Users", value: "2,405", trend: "+4.1%", isPositive: true) KPICard(title: "Churn Rate", value: "1.2%", trend: "-0.4%", isPositive: false) KPICard(title: "Avg. Session", value: "4m 12s", trend: "+0.1%", isPositive: true) } .padding() // Placeholder for Chart RoundedRectangle(cornerRadius: 12) .fill(Color.white) .frame(height: 250) .overlay(Text("Chart Area").foregroundColor(.gray)) .padding(.horizontal) } .background(Color(UIColor.systemGroupedBackground)) .navigationTitle("Overview") } } } struct KPICard: View { let title: String let value: String let trend: String let isPositive: Bool var body: some View { VStack(alignment: .leading, spacing: 8) { Text(title).font(.subheadline).foregroundColor(.secondary) Text(value).font(.title2).fontWeight(.bold) Text(trend) .font(.caption) .fontWeight(.semibold) .foregroundColor(isPositive ? .green : .red) } .padding() .frame(maxWidth: .infinity, alignment: .leading) .background(Color.white) .cornerRadius(12) .shadow(color: Color.black.opacity(0.02), radius: 4, y: 2) } }
.adaptive grids. LazyVGrid handles rearranging 4 cards in a row on iPad down to 2 cards on iPhone automatically.Color(UIColor.systemGroupedBackground) to provide that subtle off-white contrast against stark white cards.dartclass DashboardScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8F9FA), appBar: AppBar( title: const Text('Overview', style: TextStyle(color: Colors.black)), backgroundColor: Colors.white, elevation: 1, ), // On tablets, use a Row with NavigationRail. On mobile, use Drawer. drawer: const Drawer(), body: CustomScrollView( slivers: [ SliverPadding( padding: const EdgeInsets.all(16), sliver: SliverGrid.extent( maxCrossAxisExtent: 200, // Adapts layout based on width mainAxisSpacing: 16, crossAxisSpacing: 16, childAspectRatio: 1.5, children: [ _buildKPI('Revenue', '\$45,231', '+12.5%', true), _buildKPI('Active Users', '2,405', '+4.1%', true), _buildKPI('Churn Rate', '1.2%', '-0.4%', false), _buildKPI('Avg. Session', '4m 12s', '+0.1%', true), ], ), ), SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 16), sliver: SliverToBoxAdapter( child: Container( height: 250, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)), child: const Center(child: Text('Chart Area', style: TextStyle(color: Colors.grey))), ), ), ), ], ), ); } Widget _buildKPI(String title, String value, String trend, bool isPositive) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text(title, style: const TextStyle(color: Colors.grey, fontSize: 14)), const SizedBox(height: 8), Text(value, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), Text(trend, style: TextStyle(color: isPositive ? Colors.green : Colors.red, fontWeight: FontWeight.w600)), ], ), ); } }
SliverGrid.extent with a maxCrossAxisExtent is the responsive magic bullet for Flutter dashboards. It handles varying screen widths flawlessly.fl_chart package is the gold standard in Flutter.jsxconst DashboardScreen = () => { return ( <ScrollView style={{ flex: 1, backgroundColor: '#F8F9FA' }}> <View style={{ padding: 16, flexDirection: 'row', flexWrap: 'wrap', gap: 16 }}> <KPICard title="Revenue" value="$45,231" trend="+12.5%" isPositive={true} /> <KPICard title="Users" value="2,405" trend="+4.1%" isPositive={true} /> <KPICard title="Churn" value="1.2%" trend="-0.4%" isPositive={false} /> <KPICard title="Session" value="4m 12s" trend="+0.1%" isPositive={true} /> </View> <View style={{ paddingHorizontal: 16, paddingBottom: 32 }}> <View style={{ height: 250, backgroundColor: '#FFF', borderRadius: 12, justifyContent: 'center', alignItems: 'center' }}> <Text style={{ color: '#999' }}>Chart Area</Text> </View> </View> </ScrollView> ); }; const KPICard = ({ title, value, trend, isPositive }) => ( <View style={{ backgroundColor: '#FFF', padding: 16, borderRadius: 8, borderWidth: 1, borderColor: '#E9ECEF', flexBasis: '47%', // roughly half width minus gap minWidth: 150 }}> <Text style={{ color: '#6C757D', fontSize: 14, marginBottom: 4 }}>{title}</Text> <Text style={{ fontSize: 22, fontWeight: '700', marginBottom: 4 }}>{value}</Text> <Text style={{ color: isPositive ? '#28A745' : '#DC3545', fontWeight: '600', fontSize: 12 }}>{trend}</Text> </View> );
flexDirection: 'row', flexWrap: 'wrap', and set the children to flexBasis: '47%'.victory-native or @shopify/react-native-skia.kotlin@Composable fun DashboardScreen() { Column( modifier = Modifier .fillMaxSize() .background(Color(0xFFF8F9FA)) .verticalScroll(rememberScrollState()) ) { // Top App Bar substitute Text( text = "Overview", style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold, modifier = Modifier.padding(16.dp) ) // KPI Grid LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 150.dp), contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.heightIn(max = 400.dp) // Bound the grid height in scrollview ) { item { KPICard("Revenue", "$45,231", "+12.5%", true) } item { KPICard("Active Users", "2,405", "+4.1%", true) } item { KPICard("Churn Rate", "1.2%", "-0.4%", false) } item { KPICard("Avg. Session", "4m 12s", "+0.1%", true) } } Spacer(Modifier.height(16.dp)) // Chart Area Box( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) .height(250.dp) .background(Color.White, RoundedCornerShape(12.dp)) .border(1.dp, Color(0xFFE9ECEF), RoundedCornerShape(12.dp)), contentAlignment = Alignment.Center ) { Text("Chart Area", color = Color.Gray) } Spacer(Modifier.height(32.dp)) } } @Composable fun KPICard(title: String, value: String, trend: String, isPositive: Boolean) { Column( modifier = Modifier .background(Color.White, RoundedCornerShape(8.dp)) .border(1.dp, Color(0xFFE9ECEF), RoundedCornerShape(8.dp)) .padding(16.dp) ) { Text(title, color = Color.Gray, fontSize = 14.sp) Spacer(Modifier.height(4.dp)) Text(value, fontSize = 22.sp, fontWeight = FontWeight.Bold) Spacer(Modifier.height(4.dp)) Text(trend, color = if (isPositive) Color(0xFF28A745) else Color(0xFFDC3545), fontWeight = FontWeight.SemiBold, fontSize = 12.sp) } }
GridCells.Adaptive(minSize = 150.dp) creates the responsive card layout automatically.LazyVerticalGrid inside a Column with .verticalScroll can cause height calculation issues. You must use .heightIn(max=...) on the grid, or completely convert the entire layout to a single LazyVerticalGrid where the chart is just a GridItemSpan(maxLineSpan) element.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 33,186 | 42,419 | +28% | 1 | 1 | 0% | 8,240 | 11,693 | +42% | 0 | 0 | — |
case-02 | fail→pass | 21,207 | 29,769 | +40% | 1 | 1 | 0% | 4,553 | 10,144 | +123% | 0 | 0 | — |
case-03 | fail→pass | 25,343 | 30,137 | +19% | 1 | 1 | 0% | 4,195 | 9,892 | +136% | 0 | 0 | — |
case-04 | fail→fail | 25,732 | 34,863 | +35% | 1 | 1 | 0% | 5,616 | 9,251 | +65% | 0 | 0 | — |
case-05 | fail→fail | 22,714 | 26,059 | +15% | 1 | 1 | 0% | 4,898 | 9,090 | +86% | 0 | 0 | — |
case-06 | pass→pass | 10,610 | 12,346 | +16% | 1 | 1 | 0% | 1,508 | 5,778 | +283% | 0 | 0 | — |
case-07 | pass→pass | 14,867 | 12,001 | -19% | 1 | 1 | 0% | 2,287 | 5,621 | +146% | 0 | 0 | — |
case-08 | pass→pass | 26,634 | 15,378 | -42% | 1 | 1 | 0% | 2,759 | 5,700 | +107% | 0 | 0 | — |
case-09 | pass→pass | 11,522 | 3,211 | -72% | 1 | 1 | 0% | 1,612 | 3,859 | +139% | 0 | 0 | — |
case-10 | pass→pass | 15,236 | 19,747 | +30% | 1 | 1 | 0% | 1,976 | 7,549 | +282% | 0 | 0 | — |
case-11 | pass→pass | 16,203 | 13,584 | -16% | 1 | 1 | 0% | 2,414 | 5,526 | +129% | 0 | 0 | — |
case-12 | pass→pass | 15,230 | 5,814 | -62% | 1 | 1 | 0% | 2,142 | 4,410 | +106% | 0 | 0 | — |
case-22 | pass→pass | 13,072 | 19,597 | +50% | 1 | 1 | 0% | 2,742 | 6,766 | +147% | 0 | 0 | — |
case-13 | pass→pass | 15,333 | 4,062 | -74% | 1 | 1 | 0% | 1,241 | 4,199 | +238% | 0 | 0 | — |
case-14 | pass→pass | 15,551 | 18,757 | +21% | 1 | 1 | 0% | 2,570 | 6,509 | +153% | 0 | 0 | — |
case-15 | fail→pass | 14,782 | 12,380 | -16% | 1 | 1 | 0% | 2,640 | 5,345 | +102% | 0 | 0 | — |
case-16 | pass→pass | 16,510 | 12,371 | -25% | 1 | 1 | 0% | 2,504 | 5,769 | +130% | 0 | 0 | — |
case-17 | pass→pass | 16,071 | 11,690 | -27% | 1 | 1 | 0% | 2,827 | 5,683 | +101% | 0 | 0 | — |
case-18 | pass→pass | 18,358 | 21,240 | +16% | 1 | 1 | 0% | 2,723 | 6,792 | +149% | 0 | 0 | — |
case-19 | fail→pass | 12,376 | 2,604 | -79% | 1 | 1 | 0% | 1,773 | 3,819 | +115% | 0 | 0 | — |
case-20 | pass→pass | 19,456 | 38,273 | +97% | 1 | 1 | 0% | 3,103 | 10,001 | +222% | 0 | 0 | — |
case-21 | pass→pass | 25,830 | 22,257 | -14% | 1 | 1 | 0% | 4,694 | 7,522 | +60% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.