or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

content-management.mdcore-components.mdindex.mdindicators-submenus.mdinteractive-elements.mdutility-functions.md

indicators-submenus.mddocs/

0

# Visual Indicators and Sub-menus

1

2

Indicator component for visual feedback and sub-menu support for nested navigation structures.

3

4

## Capabilities

5

6

### NavigationMenuIndicator

7

8

Visual indicator component that shows the active menu item with automatic positioning and animation support.

9

10

```typescript { .api }

11

/**

12

* Visual indicator that highlights the active menu item

13

* Automatically positions itself relative to the active trigger

14

* @param forceMount - Force mounting for animation control

15

*/

16

const NavigationMenuIndicator: React.ForwardRefExoticComponent<

17

NavigationMenuIndicatorProps & React.RefAttributes<HTMLDivElement>

18

>;

19

20

interface NavigationMenuIndicatorProps

21

extends React.ComponentPropsWithoutRef<"div"> {

22

forceMount?: true;

23

}

24

```

25

26

**Key Features:**

27

- Automatic positioning based on active trigger

28

- Smooth transitions between different menu items

29

- Portal rendering to indicator track

30

- Orientation support (horizontal/vertical)

31

- Animation support via `forceMount`

32

- Resize observer integration for responsive positioning

33

34

**Usage Examples:**

35

36

```typescript

37

import {

38

NavigationMenu,

39

NavigationMenuList,

40

NavigationMenuItem,

41

NavigationMenuTrigger,

42

NavigationMenuContent,

43

NavigationMenuIndicator

44

} from "@radix-ui/react-navigation-menu";

45

46

// Basic indicator usage

47

function IndicatorExample() {

48

return (

49

<NavigationMenu>

50

<NavigationMenuList>

51

<NavigationMenuItem>

52

<NavigationMenuTrigger>Home</NavigationMenuTrigger>

53

<NavigationMenuContent>Home content</NavigationMenuContent>

54

</NavigationMenuItem>

55

56

<NavigationMenuItem>

57

<NavigationMenuTrigger>Products</NavigationMenuTrigger>

58

<NavigationMenuContent>Products content</NavigationMenuContent>

59

</NavigationMenuItem>

60

61

<NavigationMenuItem>

62

<NavigationMenuTrigger>Services</NavigationMenuTrigger>

63

<NavigationMenuContent>Services content</NavigationMenuContent>

64

</NavigationMenuItem>

65

66

{/* Indicator automatically positions over active trigger */}

67

<NavigationMenuIndicator className="nav-indicator" />

68

</NavigationMenuList>

69

</NavigationMenu>

70

);

71

}

72

73

// Styled indicator with animations

74

function StyledIndicatorExample() {

75

return (

76

<NavigationMenu>

77

<NavigationMenuList className="nav-list">

78

<NavigationMenuItem>

79

<NavigationMenuTrigger>Tab 1</NavigationMenuTrigger>

80

<NavigationMenuContent>Content 1</NavigationMenuContent>

81

</NavigationMenuItem>

82

83

<NavigationMenuItem>

84

<NavigationMenuTrigger>Tab 2</NavigationMenuTrigger>

85

<NavigationMenuContent>Content 2</NavigationMenuContent>

86

</NavigationMenuItem>

87

88

<NavigationMenuIndicator

89

className="indicator"

90

style={{

91

bottom: 0,

92

height: "2px",

93

backgroundColor: "blue",

94

transition: "transform 200ms ease",

95

}}

96

/>

97

</NavigationMenuList>

98

</NavigationMenu>

99

);

100

}

101

102

// Force mounted indicator for custom animations

103

function AnimatedIndicatorExample() {

104

return (

105

<NavigationMenu>

106

<NavigationMenuList>

107

<NavigationMenuItem>

108

<NavigationMenuTrigger>Item 1</NavigationMenuTrigger>

109

<NavigationMenuContent>Content 1</NavigationMenuContent>

110

</NavigationMenuItem>

111

112

<NavigationMenuItem>

113

<NavigationMenuTrigger>Item 2</NavigationMenuTrigger>

114

<NavigationMenuContent>Content 2</NavigationMenuContent>

115

</NavigationMenuItem>

116

117

<NavigationMenuIndicator

118

forceMount

119

className="custom-animated-indicator"

120

/>

121

</NavigationMenuList>

122

</NavigationMenu>

123

);

124

}

125

```

126

127

### Vertical Orientation Indicator

128

129

```typescript

130

function VerticalIndicatorExample() {

131

return (

132

<NavigationMenu orientation="vertical">

133

<NavigationMenuList>

134

<NavigationMenuItem>

135

<NavigationMenuTrigger>Vertical Item 1</NavigationMenuTrigger>

136

<NavigationMenuContent>Content 1</NavigationMenuContent>

137

</NavigationMenuItem>

138

139

<NavigationMenuItem>

140

<NavigationMenuTrigger>Vertical Item 2</NavigationMenuTrigger>

141

<NavigationMenuContent>Content 2</NavigationMenuContent>

142

</NavigationMenuItem>

143

144

{/* Indicator adapts to vertical orientation */}

145

<NavigationMenuIndicator

146

style={{

147

left: 0,

148

width: "3px",

149

backgroundColor: "green",

150

transition: "transform 200ms ease",

151

}}

152

/>

153

</NavigationMenuList>

154

</NavigationMenu>

155

);

156

}

157

```

158

159

## Sub-menu Architecture

160

161

Sub-menus inherit from the same core architecture but operate within a nested context:

162

163

### Basic Sub-menu Structure

164

165

```typescript

166

import {

167

NavigationMenu,

168

NavigationMenuList,

169

NavigationMenuItem,

170

NavigationMenuTrigger,

171

NavigationMenuContent,

172

NavigationMenuSub

173

} from "@radix-ui/react-navigation-menu";

174

175

function SubMenuExample() {

176

return (

177

<NavigationMenu>

178

<NavigationMenuList>

179

<NavigationMenuItem>

180

<NavigationMenuTrigger>Main Category</NavigationMenuTrigger>

181

<NavigationMenuContent>

182

{/* Sub-menu within content */}

183

<NavigationMenuSub>

184

<NavigationMenuList>

185

<NavigationMenuItem>

186

<NavigationMenuTrigger>Subcategory 1</NavigationMenuTrigger>

187

<NavigationMenuContent>

188

Sub-content 1

189

</NavigationMenuContent>

190

</NavigationMenuItem>

191

192

<NavigationMenuItem>

193

<NavigationMenuTrigger>Subcategory 2</NavigationMenuTrigger>

194

<NavigationMenuContent>

195

Sub-content 2

196

</NavigationMenuContent>

197

</NavigationMenuItem>

198

</NavigationMenuList>

199

</NavigationMenuSub>

200

</NavigationMenuContent>

201

</NavigationMenuItem>

202

</NavigationMenuList>

203

</NavigationMenu>

204

);

205

}

206

```

207

208

### Controlled Sub-menu

209

210

```typescript

211

function ControlledSubMenuExample() {

212

const [subValue, setSubValue] = React.useState("");

213

214

return (

215

<NavigationMenu>

216

<NavigationMenuList>

217

<NavigationMenuItem>

218

<NavigationMenuTrigger>Products</NavigationMenuTrigger>

219

<NavigationMenuContent>

220

<NavigationMenuSub

221

value={subValue}

222

onValueChange={setSubValue}

223

orientation="vertical"

224

>

225

<NavigationMenuList>

226

<NavigationMenuItem value="web">

227

<NavigationMenuTrigger>Web Products</NavigationMenuTrigger>

228

<NavigationMenuContent>

229

Web product details

230

</NavigationMenuContent>

231

</NavigationMenuItem>

232

233

<NavigationMenuItem value="mobile">

234

<NavigationMenuTrigger>Mobile Products</NavigationMenuTrigger>

235

<NavigationMenuContent>

236

Mobile product details

237

</NavigationMenuContent>

238

</NavigationMenuItem>

239

</NavigationMenuList>

240

</NavigationMenuSub>

241

</NavigationMenuContent>

242

</NavigationMenuItem>

243

</NavigationMenuList>

244

</NavigationMenu>

245

);

246

}

247

```

248

249

### Complex Nested Structure

250

251

```typescript

252

function ComplexNestedExample() {

253

return (

254

<NavigationMenu>

255

<NavigationMenuList>

256

<NavigationMenuItem>

257

<NavigationMenuTrigger>Solutions</NavigationMenuTrigger>

258

<NavigationMenuContent>

259

<div className="mega-menu">

260

<div className="menu-column">

261

<h3>By Industry</h3>

262

<NavigationMenuSub>

263

<NavigationMenuList>

264

<NavigationMenuItem>

265

<NavigationMenuTrigger>Healthcare</NavigationMenuTrigger>

266

<NavigationMenuContent>

267

<NavigationMenuLink href="/healthcare/hospitals">Hospitals</NavigationMenuLink>

268

<NavigationMenuLink href="/healthcare/clinics">Clinics</NavigationMenuLink>

269

</NavigationMenuContent>

270

</NavigationMenuItem>

271

272

<NavigationMenuItem>

273

<NavigationMenuTrigger>Finance</NavigationMenuTrigger>

274

<NavigationMenuContent>

275

<NavigationMenuLink href="/finance/banking">Banking</NavigationMenuLink>

276

<NavigationMenuLink href="/finance/insurance">Insurance</NavigationMenuLink>

277

</NavigationMenuContent>

278

</NavigationMenuItem>

279

</NavigationMenuList>

280

</NavigationMenuSub>

281

</div>

282

283

<div className="menu-column">

284

<h3>By Size</h3>

285

<NavigationMenuSub>

286

<NavigationMenuList>

287

<NavigationMenuItem>

288

<NavigationMenuLink href="/enterprise">Enterprise</NavigationMenuLink>

289

</NavigationMenuItem>

290

291

<NavigationMenuItem>

292

<NavigationMenuLink href="/small-business">Small Business</NavigationMenuLink>

293

</NavigationMenuItem>

294

</NavigationMenuList>

295

</NavigationMenuSub>

296

</div>

297

</div>

298

</NavigationMenuContent>

299

</NavigationMenuItem>

300

</NavigationMenuList>

301

</NavigationMenu>

302

);

303

}

304

```

305

306

## Indicator Positioning

307

308

The indicator automatically handles positioning based on the active trigger:

309

310

### Horizontal Layout

311

- Uses `transform: translateX()` for positioning

312

- Width matches the active trigger width

313

- Positioned at bottom by default

314

315

### Vertical Layout

316

- Uses `transform: translateY()` for positioning

317

- Height matches the active trigger height

318

- Positioned at left by default

319

320

### CSS Example

321

322

```css

323

/* Horizontal indicator */

324

.nav-indicator[data-orientation="horizontal"] {

325

bottom: 0;

326

height: 2px;

327

background: var(--accent-color);

328

transition: transform 200ms ease;

329

}

330

331

/* Vertical indicator */

332

.nav-indicator[data-orientation="vertical"] {

333

left: 0;

334

width: 3px;

335

background: var(--accent-color);

336

transition: transform 200ms ease;

337

}

338

339

/* Indicator states */

340

.nav-indicator[data-state="visible"] {

341

opacity: 1;

342

}

343

344

.nav-indicator[data-state="hidden"] {

345

opacity: 0;

346

}

347

```

348

349

## Data Attributes

350

351

Both indicators and sub-menus expose data attributes for styling:

352

353

### NavigationMenuIndicator Data Attributes

354

355

```css

356

[data-state="visible"] { /* Indicator is shown */ }

357

[data-state="hidden"] { /* Indicator is hidden */ }

358

[data-orientation="horizontal"] { /* Horizontal layout */ }

359

[data-orientation="vertical"] { /* Vertical layout */ }

360

```

361

362

**Advanced Indicator Styling:**

363

364

```css

365

.nav-indicator {

366

position: absolute;

367

transition: all 200ms ease;

368

}

369

370

.nav-indicator[data-state="visible"] {

371

opacity: 1;

372

transform: scale(1);

373

}

374

375

.nav-indicator[data-state="hidden"] {

376

opacity: 0;

377

transform: scale(0.8);

378

}

379

380

.nav-indicator[data-orientation="horizontal"] {

381

width: 100%;

382

height: 2px;

383

bottom: 0;

384

left: 0;

385

}

386

387

.nav-indicator[data-orientation="vertical"] {

388

width: 3px;

389

height: 100%;

390

left: 0;

391

top: 0;

392

}

393

```

394

395

### NavigationMenuSub Data Attributes

396

397

```css

398

[data-orientation="horizontal"] { /* Sub-menu horizontal layout */ }

399

[data-orientation="vertical"] { /* Sub-menu vertical layout */ }

400

```

401

402

**Sub-menu Styling:**

403

404

```css

405

.nav-sub[data-orientation="horizontal"] {

406

display: flex;

407

flex-direction: row;

408

}

409

410

.nav-sub[data-orientation="vertical"] {

411

display: flex;

412

flex-direction: column;

413

}

414

```

415

416

These attributes enable CSS-based styling and animations without JavaScript.