| name | widget-based-design |
| description | Web and App implementation guide for Widget-Based Design. Trigger when user wants modular blocks, iOS Home Screen aesthetics, and customizable mini-apps. |
| date_added | 2026-06-17 |
| risk | safe |
| source | self |
| source_type | self |
Widget-Based Design
"Miniature applications. Small, highly functional blocks of UI designed to be rearranged."
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
- Strict Aspect Ratios: Widgets usually follow strict sizes (1x1 square, 2x1 rectangle, 2x2 large square).
- Glanceability: Widgets show the most important piece of data instantly. Deep interaction usually requires opening the full app.
- Corner Radius Match: The inner content's border radius should perfectly nest within the widget's outer border radius.
Visual DNA
- Colors: Widgets often use bright, solid color backgrounds or full-bleed photos to differentiate themselves.
- Typography: Large, bold numbers (like a clock or weather temp) paired with tiny sub-labels.
- Layout: Similar to Bento UI, but specifically focused on functional app-lets rather than just content layout.
Web Implementation
.widget-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
grid-auto-rows: 160px;
gap: 16px;
padding: 32px;
}
.widget {
background-color: #ffffff;
border-radius: 24px;
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
padding: 16px;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
position: relative;
}
.widget-small { grid-column: span 1; grid-row: span 1; }
.widget-medium { grid-column: span 2; grid-row: span 1; }
.widget-large { grid-column: span 2; grid-row: span 2; }
{
: (, , );
: white;
}
{ : ; : ; }
{ : absolute; : ; : ; : ; }
App Implementation
SwiftUI
struct WidgetDesignView: View {
let columns = [GridItem(.flexible(), spacing: 16), GridItem(.flexible(), spacing: 16)]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
WeatherWidget()
.frame(height: 160)
FitnessWidget()
.frame(height: 160)
MusicWidget()
.frame(height: 160)
}
.padding(24)
}
.background(Color(white: 0.95))
}
}
struct WeatherWidget: View {
var body: some View {
ZStack(alignment: .topTrailing) {
LinearGradient(colors: [Color(hex: "4facfe"), Color(hex: "00f2fe")], startPoint: .topLeading, endPoint: .bottomTrailing)
Image(systemName: "cloud.sun.fill")
.foregroundColor(.white)
.font(.system(size: 40))
.padding()
(alignment: .leading) {
()
()
.font(.system(size: , weight: .thin))
.foregroundColor(.white)
()
.font(.subheadline)
.foregroundColor(.white.opacity())
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
.cornerRadius()
.shadow(color: .black.opacity(), radius: , y: )
}
}
- SwiftUI is perfect for this. The
LazyVGrid handles the layout, and cornerRadius(24) matches Apple's default widget styling perfectly.
- Use
ZStack as the root of the widget to easily overlay content on top of complex gradients or images.
Flutter
class WidgetDesignScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF2F2F2),
body: GridView.count(
crossAxisCount: 2, // 2 columns for a typical phone
padding: const EdgeInsets.all(24),
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 1.0, // 1x1 squares
children: [
// Note: Flutter GridView doesn't easily span rows/cols out of the box.
// flutter_staggered_grid_view is highly recommended for real widget layouts.
_buildWeatherWidget(),
_buildFitnessWidget(),
_buildMusicWidget(),
],
),
);
}
Widget _buildWeatherWidget() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
gradient: const LinearGradient(colors: [Color(0xFF4FACFE), Color(0xFF00F2FE)]),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 5))],
),
padding: const EdgeInsets.all(16),
child: Stack(
children: [
const Align(
alignment: Alignment.topRight,
child: Icon(Icons.cloud, color: Colors.white, size: 40),
),
Align(
alignment: Alignment.bottomLeft,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('72°', style: TextStyle(color: Colors.white, fontSize: 48, fontWeight: FontWeight.w300)),
Text('San Francisco', style: TextStyle(color: Colors.white.withOpacity(0.8), fontSize: 14)),
],
),
)
],
),
);
}
}
- A
Stack inside a Container with BorderRadius.circular(24) is the blueprint for every widget.
- Standard
GridView is too rigid for mixed 2x1 and 1x1 widgets. Use the flutter_staggered_grid_view package for production apps.
React Native
const WidgetDesignScreen = () => {
return (
<ScrollView style={{ flex: 1, backgroundColor: '#F2F2F2' }} contentContainerStyle={{ padding: 24 }}>
<View style={{ flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', gap: 16 }}>
{/* 2x1 Widget (Full width minus padding) */}
<View style={[styles.widget, { width: '100%' }]}>
<LinearGradient colors={['#4facfe', '#00f2fe']} style={styles.widgetBg} />
<Text style={styles.temp}>72°</Text>
<Text style={styles.sub}>San Francisco</Text>
</View>
{/* 1x1 Widgets (Half width minus half gap) */}
< = { '%' }]}>Fitness
Music
);
};
styles = .({
: {
: ,
: ,
: ,
: ,
: ,
: , : { : , : }, : , : , : ,
:
},
: {
....,
: ,
},
: { : , : , : },
: { : , : }
});
- In React Native,
flexWrap: 'wrap' combined with percentage widths (e.g., 47% for 2 columns with a gap) is the cleanest way to build a responsive widget layout.
- If using
LinearGradient from expo-linear-gradient or react-native-linear-gradient, apply StyleSheet.absoluteFillObject so it sits behind the text.
Jetpack Compose
@Composable
fun WidgetDesignScreen() {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.fillMaxSize().background(Color(0xFFF2F2F2)).padding(24.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item(span = { GridItemSpan(2) }) {
WeatherWidget(Modifier.height(160.dp))
}
item { Box(Modifier.height(160.dp).background(Color.White, RoundedCornerShape(24.dp))) }
item { Box(Modifier.height(160.dp).background(Color.White, RoundedCornerShape(24.dp))) }
}
}
@Composable
fun WeatherWidget(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.shadow(10.dp, RoundedCornerShape(24.dp))
.background(Brush.linearGradient(listOf(Color(0xFF4FACFE), Color(0xFF00F2FE))), RoundedCornerShape(24.dp))
.padding(16.dp)
) {
Icon(Icons.Filled.Cloud, contentDescription = null, tint = Color.White, modifier = Modifier.align(Alignment.TopEnd).size(40.dp))
Column(modifier = Modifier.align(Alignment.BottomStart)) {
Text("72°", fontSize = 48.sp, fontWeight = FontWeight.Light, color = Color.White)
Text("San Francisco", fontSize = 14.sp, color = Color.White.copy(alpha = ))
}
}
}
LazyVerticalGrid with GridCells.Fixed(2) and GridItemSpan perfectly recreate iOS widget grids.
RoundedCornerShape(24.dp) is essential to sell the look.
Do's and Don'ts
- DO: Use full-bleed background colors to make different widgets stand out.
- DON'T: Put complex forms or scrollable lists inside a 1x1 widget.
Limitations
- This is a styling reference and does not replace enprojectnment-specific validation, accessibility testing, or expert review.
- Ensure appropriate contrast ratios and responsive behaviors are verified separately.