or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-apis.mdbrowser-events.mddevice-sensors.mdelement-tracking.mdindex.mdmouse-pointer.mdscroll-resize.mdtheme-preferences.mdutilities-advanced.mdwindow-document.md

window-document.mddocs/

0

# Window and Document

1

2

Components for tracking window and document state, focus, visibility, and dimensions.

3

4

## Capabilities

5

6

### UseWindowSize Component

7

8

Tracks window dimensions with reactive updates.

9

10

```typescript { .api }

11

/**

12

* Component that tracks window dimensions

13

* @example

14

* <UseWindowSize v-slot="{ width, height }">

15

* <div>Window size: {{ width }} Γ— {{ height }}</div>

16

* </UseWindowSize>

17

*/

18

interface UseWindowSizeProps {

19

/** Initial width value @default Infinity */

20

initialWidth?: number;

21

/** Initial height value @default Infinity */

22

initialHeight?: number;

23

/** Listen to orientationchange event @default true */

24

listenOrientation?: boolean;

25

/** Include scrollbars in dimensions @default true */

26

includeScrollbar?: boolean;

27

/** Window object @default defaultWindow */

28

window?: Window;

29

}

30

31

/** Slot data exposed by UseWindowSize component */

32

interface UseWindowSizeReturn {

33

/** Current window width */

34

width: Ref<number>;

35

/** Current window height */

36

height: Ref<number>;

37

}

38

```

39

40

**Usage Examples:**

41

42

```vue

43

<template>

44

<!-- Basic window size tracking -->

45

<UseWindowSize v-slot="{ width, height }">

46

<div class="window-info">

47

<h3>Window Information</h3>

48

<div class="size-display">

49

<div class="dimension">

50

<span class="label">Width:</span>

51

<span class="value">{{ width }}px</span>

52

</div>

53

<div class="dimension">

54

<span class="label">Height:</span>

55

<span class="value">{{ height }}px</span>

56

</div>

57

<div class="dimension">

58

<span class="label">Aspect Ratio:</span>

59

<span class="value">{{ (width / height).toFixed(2) }}</span>

60

</div>

61

</div>

62

</div>

63

</UseWindowSize>

64

65

<!-- Responsive breakpoint detection -->

66

<UseWindowSize v-slot="{ width, height }">

67

<div class="breakpoint-info">

68

<h3>Responsive Breakpoints</h3>

69

<div class="breakpoint" :class="getBreakpointClass(width)">

70

<span class="breakpoint-name">{{ getBreakpointName(width) }}</span>

71

<span class="breakpoint-size">{{ width }}px</span>

72

</div>

73

<div class="orientation">

74

Orientation: {{ width > height ? 'Landscape' : 'Portrait' }}

75

</div>

76

</div>

77

</UseWindowSize>

78

79

<!-- Window size visualization -->

80

<UseWindowSize v-slot="{ width, height }">

81

<div class="size-viz">

82

<h3>Size Visualization</h3>

83

<div

84

class="window-preview"

85

:style="{

86

width: Math.max(50, width / 10) + 'px',

87

height: Math.max(30, height / 10) + 'px'

88

}"

89

>

90

<span class="preview-text">{{ width }}Γ—{{ height }}</span>

91

</div>

92

</div>

93

</UseWindowSize>

94

</template>

95

96

<script setup>

97

import { UseWindowSize } from '@vueuse/components';

98

99

function getBreakpointName(width) {

100

if (width < 640) return 'Mobile';

101

if (width < 768) return 'Small Tablet';

102

if (width < 1024) return 'Tablet';

103

if (width < 1280) return 'Desktop';

104

return 'Large Desktop';

105

}

106

107

function getBreakpointClass(width) {

108

if (width < 640) return 'mobile';

109

if (width < 768) return 'sm';

110

if (width < 1024) return 'tablet';

111

if (width < 1280) return 'desktop';

112

return 'xl';

113

}

114

</script>

115

116

<style>

117

.window-info, .breakpoint-info, .size-viz {

118

border: 2px solid #ddd;

119

border-radius: 8px;

120

padding: 20px;

121

margin: 15px 0;

122

}

123

124

.size-display {

125

display: grid;

126

grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));

127

gap: 15px;

128

margin-top: 15px;

129

}

130

131

.dimension {

132

display: flex;

133

flex-direction: column;

134

align-items: center;

135

padding: 10px;

136

background: #f5f5f5;

137

border-radius: 6px;

138

}

139

140

.label {

141

font-size: 0.9em;

142

color: #666;

143

margin-bottom: 5px;

144

}

145

146

.value {

147

font-size: 1.5em;

148

font-weight: bold;

149

color: #2196f3;

150

}

151

152

.breakpoint {

153

display: flex;

154

justify-content: space-between;

155

align-items: center;

156

padding: 15px;

157

border-radius: 8px;

158

margin: 15px 0;

159

font-weight: bold;

160

}

161

162

.breakpoint.mobile { background: #ffebee; }

163

.breakpoint.sm { background: #e8f5e8; }

164

.breakpoint.tablet { background: #e3f2fd; }

165

.breakpoint.desktop { background: #f3e5f5; }

166

.breakpoint.xl { background: #fff3e0; }

167

168

.window-preview {

169

border: 2px solid #2196f3;

170

background: #e3f2fd;

171

border-radius: 4px;

172

display: flex;

173

align-items: center;

174

justify-content: center;

175

min-width: 50px;

176

min-height: 30px;

177

margin: 15px 0;

178

}

179

180

.preview-text {

181

font-size: 10px;

182

font-weight: bold;

183

color: #1976d2;

184

}

185

</style>

186

```

187

188

### UseWindowFocus Component

189

190

Tracks window focus state.

191

192

```typescript { .api }

193

/**

194

* Component that tracks window focus state

195

* @example

196

* <UseWindowFocus v-slot="{ focused }">

197

* <div>Window focused: {{ focused ? 'Yes' : 'No' }}</div>

198

* </UseWindowFocus>

199

*/

200

interface UseWindowFocusProps {

201

/** Window object @default defaultWindow */

202

window?: Window;

203

}

204

205

/** Slot data exposed by UseWindowFocus component */

206

interface UseWindowFocusReturn {

207

/** Whether window is focused */

208

focused: Ref<boolean>;

209

}

210

```

211

212

**Usage Examples:**

213

214

```vue

215

<template>

216

<!-- Basic focus tracking -->

217

<UseWindowFocus v-slot="{ focused }">

218

<div class="focus-indicator" :class="{ focused, blurred: !focused }">

219

<div class="status-icon">

220

{{ focused ? 'πŸ‘οΈ' : '😴' }}

221

</div>

222

<div class="status-text">

223

Window is {{ focused ? 'focused' : 'blurred' }}

224

</div>

225

<div class="hint">

226

{{ focused ? 'You are viewing this tab' : 'Switch to another tab to see blur state' }}

227

</div>

228

</div>

229

</UseWindowFocus>

230

231

<!-- Focus-based features -->

232

<UseWindowFocus v-slot="{ focused }">

233

<div class="focus-features">

234

<h3>Focus-Aware Features</h3>

235

<div class="feature" :class="{ active: focused }">

236

<span class="feature-name">Auto-refresh</span>

237

<span class="feature-status">{{ focused ? 'Active' : 'Paused' }}</span>

238

</div>

239

<div class="feature" :class="{ active: focused }">

240

<span class="feature-name">Real-time updates</span>

241

<span class="feature-status">{{ focused ? 'Enabled' : 'Disabled' }}</span>

242

</div>

243

<div class="feature" :class="{ active: !focused }">

244

<span class="feature-name">Battery saver</span>

245

<span class="feature-status">{{ !focused ? 'Active' : 'Inactive' }}</span>

246

</div>

247

</div>

248

</UseWindowFocus>

249

250

<!-- Notification helper -->

251

<UseWindowFocus v-slot="{ focused }">

252

<div class="notification-helper">

253

<h3>Smart Notifications</h3>

254

<p v-if="!focused" class="notification-tip">

255

πŸ”” Perfect time to show notifications - user is not actively viewing

256

</p>

257

<p v-else class="notification-tip">

258

πŸ‘€ User is active - consider in-app notifications instead

259

</p>

260

<button @click="simulateNotification(focused)">

261

Test Notification Strategy

262

</button>

263

</div>

264

</UseWindowFocus>

265

</template>

266

267

<script setup>

268

import { UseWindowFocus } from '@vueuse/components';

269

270

function simulateNotification(focused) {

271

if (focused) {

272

alert('In-app notification (user is active)');

273

} else {

274

console.log('Browser notification sent (user is away)');

275

}

276

}

277

</script>

278

279

<style>

280

.focus-indicator {

281

padding: 20px;

282

border-radius: 8px;

283

text-align: center;

284

transition: all 0.3s;

285

border: 2px solid #ddd;

286

}

287

288

.focus-indicator.focused {

289

background: #e8f5e8;

290

border-color: #4caf50;

291

}

292

293

.focus-indicator.blurred {

294

background: #ffebee;

295

border-color: #f44336;

296

}

297

298

.status-icon {

299

font-size: 2em;

300

margin-bottom: 10px;

301

}

302

303

.status-text {

304

font-size: 1.2em;

305

font-weight: bold;

306

margin-bottom: 5px;

307

}

308

309

.hint {

310

font-size: 0.9em;

311

color: #666;

312

font-style: italic;

313

}

314

315

.focus-features {

316

border: 1px solid #ddd;

317

border-radius: 8px;

318

padding: 20px;

319

}

320

321

.feature {

322

display: flex;

323

justify-content: space-between;

324

align-items: center;

325

padding: 10px;

326

margin: 8px 0;

327

border-radius: 6px;

328

background: #f5f5f5;

329

transition: background 0.3s;

330

}

331

332

.feature.active {

333

background: #e8f5e8;

334

}

335

336

.feature-name {

337

font-weight: bold;

338

}

339

340

.feature-status {

341

font-size: 0.9em;

342

color: #666;

343

}

344

345

.notification-helper {

346

border: 1px solid #ddd;

347

border-radius: 8px;

348

padding: 20px;

349

}

350

351

.notification-tip {

352

padding: 10px;

353

border-radius: 6px;

354

background: #f0f8ff;

355

border-left: 4px solid #2196f3;

356

margin: 15px 0;

357

}

358

</style>

359

```

360

361

### UseDocumentVisibility Component

362

363

Tracks document visibility state using the Page Visibility API.

364

365

```typescript { .api }

366

/**

367

* Component that tracks document visibility state

368

* @example

369

* <UseDocumentVisibility v-slot="{ visibility }">

370

* <div>Page visibility: {{ visibility }}</div>

371

* </UseDocumentVisibility>

372

*/

373

interface UseDocumentVisibilityProps {

374

/** Document object @default defaultDocument */

375

document?: Document;

376

}

377

378

/** Slot data exposed by UseDocumentVisibility component */

379

interface UseDocumentVisibilityReturn {

380

/** Current document visibility state */

381

visibility: Ref<DocumentVisibilityState>;

382

}

383

384

type DocumentVisibilityState = 'visible' | 'hidden' | 'prerender';

385

```

386

387

**Usage Examples:**

388

389

```vue

390

<template>

391

<!-- Basic visibility tracking -->

392

<UseDocumentVisibility v-slot="{ visibility }">

393

<div class="visibility-tracker" :class="visibility">

394

<div class="visibility-indicator">

395

<span class="status-dot"></span>

396

<span class="status-text">{{ getVisibilityText(visibility) }}</span>

397

</div>

398

<div class="visibility-description">

399

{{ getVisibilityDescription(visibility) }}

400

</div>

401

</div>

402

</UseDocumentVisibility>

403

404

<!-- Performance optimization guide -->

405

<UseDocumentVisibility v-slot="{ visibility }">

406

<div class="performance-guide">

407

<h3>Performance Optimization</h3>

408

<div class="optimization-item" :class="{ active: visibility === 'visible' }">

409

<span class="opt-name">Animations</span>

410

<span class="opt-status">{{ visibility === 'visible' ? 'Running' : 'Paused' }}</span>

411

</div>

412

<div class="optimization-item" :class="{ active: visibility === 'visible' }">

413

<span class="opt-name">API Polling</span>

414

<span class="opt-status">{{ visibility === 'visible' ? 'Active' : 'Reduced' }}</span>

415

</div>

416

<div class="optimization-item" :class="{ active: visibility === 'hidden' }">

417

<span class="opt-name">Background Tasks</span>

418

<span class="opt-status">{{ visibility === 'hidden' ? 'Optimized' : 'Normal' }}</span>

419

</div>

420

</div>

421

</UseDocumentVisibility>

422

423

<!-- User engagement tracking -->

424

<UseDocumentVisibility v-slot="{ visibility }">

425

<div class="engagement-tracker">

426

<h3>User Engagement</h3>

427

<div class="engagement-stats">

428

<div class="stat">

429

<span class="stat-label">Current Status:</span>

430

<span class="stat-value" :class="visibility">{{ visibility }}</span>

431

</div>

432

<div class="stat">

433

<span class="stat-label">Engagement Level:</span>

434

<span class="stat-value">{{ getEngagementLevel(visibility) }}</span>

435

</div>

436

</div>

437

<div class="engagement-actions">

438

<button

439

@click="logEngagement(visibility)"

440

:disabled="visibility !== 'visible'"

441

>

442

Log User Action

443

</button>

444

</div>

445

</div>

446

</UseDocumentVisibility>

447

</template>

448

449

<script setup>

450

import { UseDocumentVisibility } from '@vueuse/components';

451

452

function getVisibilityText(visibility) {

453

switch (visibility) {

454

case 'visible': return 'Page is Visible';

455

case 'hidden': return 'Page is Hidden';

456

case 'prerender': return 'Page is Pre-rendering';

457

default: return 'Unknown State';

458

}

459

}

460

461

function getVisibilityDescription(visibility) {

462

switch (visibility) {

463

case 'visible':

464

return 'The page is currently visible and active.';

465

case 'hidden':

466

return 'The page is hidden (minimized, in background tab, etc.).';

467

case 'prerender':

468

return 'The page is being pre-rendered in the background.';

469

default:

470

return 'Unable to determine page visibility state.';

471

}

472

}

473

474

function getEngagementLevel(visibility) {

475

switch (visibility) {

476

case 'visible': return 'High';

477

case 'hidden': return 'Low';

478

case 'prerender': return 'None';

479

default: return 'Unknown';

480

}

481

}

482

483

function logEngagement(visibility) {

484

console.log(`User action logged - Page visibility: ${visibility}`);

485

}

486

</script>

487

488

<style>

489

.visibility-tracker {

490

padding: 20px;

491

border-radius: 8px;

492

border: 2px solid #ddd;

493

transition: all 0.3s;

494

}

495

496

.visibility-tracker.visible {

497

background: #e8f5e8;

498

border-color: #4caf50;

499

}

500

501

.visibility-tracker.hidden {

502

background: #ffebee;

503

border-color: #f44336;

504

}

505

506

.visibility-tracker.prerender {

507

background: #fff3e0;

508

border-color: #ff9800;

509

}

510

511

.visibility-indicator {

512

display: flex;

513

align-items: center;

514

gap: 10px;

515

font-size: 1.2em;

516

font-weight: bold;

517

margin-bottom: 10px;

518

}

519

520

.status-dot {

521

width: 12px;

522

height: 12px;

523

border-radius: 50%;

524

background: currentColor;

525

}

526

527

.visibility-description {

528

font-size: 0.9em;

529

color: #666;

530

}

531

532

.performance-guide, .engagement-tracker {

533

border: 1px solid #ddd;

534

border-radius: 8px;

535

padding: 20px;

536

}

537

538

.optimization-item, .stat {

539

display: flex;

540

justify-content: space-between;

541

align-items: center;

542

padding: 8px 12px;

543

margin: 5px 0;

544

border-radius: 6px;

545

background: #f5f5f5;

546

}

547

548

.optimization-item.active {

549

background: #e3f2fd;

550

}

551

552

.engagement-stats {

553

margin: 15px 0;

554

}

555

556

.stat-value.visible {

557

color: #4caf50;

558

font-weight: bold;

559

}

560

561

.stat-value.hidden {

562

color: #f44336;

563

font-weight: bold;

564

}

565

566

.stat-value.prerender {

567

color: #ff9800;

568

font-weight: bold;

569

}

570

</style>

571

```

572

573

### UsePageLeave Component

574

575

Detects when the cursor leaves the page viewport.

576

577

```typescript { .api }

578

/**

579

* Component that detects when cursor leaves page

580

* @example

581

* <UsePageLeave v-slot="{ isLeft }">

582

* <div>Cursor left page: {{ isLeft ? 'Yes' : 'No' }}</div>

583

* </UsePageLeave>

584

*/

585

interface UsePageLeaveProps {

586

/** Window object @default defaultWindow */

587

window?: Window;

588

}

589

590

/** Slot data exposed by UsePageLeave component */

591

interface UsePageLeaveReturn {

592

/** Whether cursor has left the page */

593

isLeft: Ref<boolean>;

594

}

595

```

596

597

**Usage Examples:**

598

599

```vue

600

<template>

601

<!-- Basic page leave detection -->

602

<UsePageLeave v-slot="{ isLeft }">

603

<div class="page-leave-indicator" :class="{ left: isLeft, present: !isLeft }">

604

<div class="cursor-status">

605

<span class="cursor-icon">{{ isLeft ? 'πŸ“€' : 'πŸ“' }}</span>

606

<span class="cursor-text">

607

Cursor {{ isLeft ? 'left the page' : 'is on the page' }}

608

</span>

609

</div>

610

</div>

611

</UsePageLeave>

612

613

<!-- Exit intent detection -->

614

<UsePageLeave v-slot="{ isLeft }">

615

<div class="exit-intent" v-if="isLeft">

616

<div class="exit-modal">

617

<h3>Wait! Don't leave yet! πŸ‘‹</h3>

618

<p>Are you sure you want to leave? You might miss out on:</p>

619

<ul>

620

<li>✨ Special offers</li>

621

<li>🎯 Personalized content</li>

622

<li>πŸ“š Helpful resources</li>

623

</ul>

624

<div class="exit-actions">

625

<button @click="stayOnPage" class="stay-btn">Stay</button>

626

<button @click="dismissModal" class="leave-btn">Leave</button>

627

</div>

628

</div>

629

<div class="exit-overlay" @click="dismissModal"></div>

630

</div>

631

</UsePageLeave>

632

633

<!-- User engagement tracking -->

634

<UsePageLeave v-slot="{ isLeft }">

635

<div class="engagement-monitor">

636

<h3>Engagement Monitor</h3>

637

<div class="engagement-indicator" :class="{ engaged: !isLeft, disengaged: isLeft }">

638

<span class="status">{{ isLeft ? '⚠️ Potential Exit' : 'βœ… User Engaged' }}</span>

639

</div>

640

<div class="engagement-tips" v-if="isLeft">

641

<p>πŸ’‘ Consider:</p>

642

<ul>

643

<li>Showing exit-intent popup</li>

644

<li>Saving user progress</li>

645

<li>Triggering retention campaigns</li>

646

</ul>

647

</div>

648

</div>

649

</UsePageLeave>

650

</template>

651

652

<script setup>

653

import { ref } from 'vue';

654

import { UsePageLeave } from '@vueuse/components';

655

656

const showExitModal = ref(true);

657

658

function stayOnPage() {

659

showExitModal.value = false;

660

// Additional logic to engage user

661

}

662

663

function dismissModal() {

664

showExitModal.value = false;

665

}

666

</script>

667

668

<style>

669

.page-leave-indicator {

670

padding: 20px;

671

border-radius: 8px;

672

border: 2px solid #ddd;

673

text-align: center;

674

transition: all 0.3s;

675

}

676

677

.page-leave-indicator.present {

678

background: #e8f5e8;

679

border-color: #4caf50;

680

}

681

682

.page-leave-indicator.left {

683

background: #fff3cd;

684

border-color: #ffc107;

685

}

686

687

.cursor-status {

688

display: flex;

689

align-items: center;

690

justify-content: center;

691

gap: 10px;

692

font-size: 1.2em;

693

}

694

695

.cursor-icon {

696

font-size: 1.5em;

697

}

698

699

.exit-intent {

700

position: fixed;

701

top: 0;

702

left: 0;

703

right: 0;

704

bottom: 0;

705

z-index: 1000;

706

display: flex;

707

align-items: center;

708

justify-content: center;

709

}

710

711

.exit-overlay {

712

position: absolute;

713

top: 0;

714

left: 0;

715

right: 0;

716

bottom: 0;

717

background: rgba(0, 0, 0, 0.7);

718

}

719

720

.exit-modal {

721

position: relative;

722

background: white;

723

border-radius: 12px;

724

padding: 30px;

725

max-width: 400px;

726

box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);

727

text-align: center;

728

}

729

730

.exit-modal h3 {

731

color: #333;

732

margin-bottom: 15px;

733

}

734

735

.exit-modal ul {

736

text-align: left;

737

margin: 15px 0;

738

}

739

740

.exit-actions {

741

display: flex;

742

gap: 15px;

743

margin-top: 20px;

744

}

745

746

.stay-btn, .leave-btn {

747

flex: 1;

748

padding: 12px 20px;

749

border: none;

750

border-radius: 6px;

751

font-size: 16px;

752

cursor: pointer;

753

}

754

755

.stay-btn {

756

background: #4caf50;

757

color: white;

758

}

759

760

.leave-btn {

761

background: #f5f5f5;

762

color: #666;

763

}

764

765

.engagement-monitor {

766

border: 1px solid #ddd;

767

border-radius: 8px;

768

padding: 20px;

769

}

770

771

.engagement-indicator {

772

padding: 15px;

773

border-radius: 8px;

774

text-align: center;

775

font-weight: bold;

776

margin: 15px 0;

777

}

778

779

.engagement-indicator.engaged {

780

background: #e8f5e8;

781

color: #2e7d32;

782

}

783

784

.engagement-indicator.disengaged {

785

background: #fff3cd;

786

color: #f57c00;

787

}

788

789

.engagement-tips {

790

background: #f0f8ff;

791

border-left: 4px solid #2196f3;

792

padding: 15px;

793

margin-top: 15px;

794

}

795

796

.engagement-tips ul {

797

margin: 10px 0;

798

padding-left: 20px;

799

}

800

</style>

801

```

802

803

## Type Definitions

804

805

```typescript { .api }

806

/** Common types used across window and document components */

807

type MaybeRefOrGetter<T> = T | Ref<T> | (() => T);

808

809

interface RenderableComponent {

810

/** The element that the component should be rendered as @default 'div' */

811

as?: object | string;

812

}

813

814

/** Document visibility states */

815

type DocumentVisibilityState = 'visible' | 'hidden' | 'prerender';

816

817

/** Window size information */

818

interface WindowSize {

819

width: number;

820

height: number;

821

}

822

823

/** Configuration interfaces */

824

interface ConfigurableWindow {

825

/** Window object @default defaultWindow */

826

window?: Window;

827

}

828

829

interface ConfigurableDocument {

830

/** Document object @default defaultDocument */

831

document?: Document;

832

}

833

```