Selecting multiple lines in Visual Studio by dragging the mouse cursor while holding a keyboard shortcut

๐Ÿ“ฆ Key shortcuts
โœจ The Prompt Phrase
What keyboard shortcut combined with a mouse drag allows you to select multiple lines at once in Visual Studio?

 ~shift + alt + "Select lines"~

๐Ÿ’ป Code Preview

๐Ÿ“ฆ All-in-One Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Selecting multiple lines in Visual Studio by dragging the mouse cursor while holding a keyboard shortcut - Interactive Tutorial</title>
<style>
  :root{
    --bg:#0b0b1a; --bg2:#12122b; --glass:rgba(255,255,255,.06); --border:rgba(255,255,255,.12);
    --text:#e6e8f5; --muted:#a3a8c8; --purple:#8b5cf6; --blue:#3b82f6; --green:#22c55e; --red:#ef4444; --amber:#f59e0b;
    --grad:linear-gradient(135deg,var(--purple),var(--blue) 55%,var(--green));
    --radius:20px;
  }
  body.light{--bg:#f4f5ff;--bg2:#e8eaff;--glass:rgba(255,255,255,.75);--border:rgba(0,0,0,.1);--text:#1e1f3a;--muted:#53577a}
  *{margin:0;padding:0;box-sizing:border-box}
  html{scroll-behavior:smooth}
  body{font-family:'Segoe UI',system-ui,sans-serif;background:radial-gradient(circle at 20% 0%,var(--bg2),var(--bg) 60%);color:var(--text);line-height:1.7;min-height:100vh}
  #progress{position:fixed;top:0;left:0;height:4px;width:0;background:var(--grad);z-index:100;transition:width .2s}
  nav{position:sticky;top:0;z-index:50;display:flex;gap:6px;align-items:center;flex-wrap:wrap;padding:10px 16px;background:var(--glass);backdrop-filter:blur(14px);border-bottom:1px solid var(--border)}
  nav a{color:var(--muted);text-decoration:none;font-size:.85rem;padding:6px 12px;border-radius:999px;transition:.2s}
  nav a:hover,nav a:focus-visible{background:var(--purple);color:#fff;outline:none}
  nav .spacer{flex:1}
  button{font-family:inherit}
  .btn{border:none;border-radius:12px;padding:10px 18px;font-weight:600;cursor:pointer;color:#fff;background:var(--grad);transition:transform .2s,opacity .2s}
  .btn:hover{transform:translateY(-2px);opacity:.92}
  .btn:focus-visible,.tab:focus-visible,.acc-head:focus-visible,.opt:focus-visible{outline:3px solid var(--amber);outline-offset:2px}
  .btn.ghost{background:var(--glass);border:1px solid var(--border);color:var(--text)}
  .btn.on{background:var(--green);color:#052e16}
  main{max-width:960px;margin:0 auto;padding:24px 16px 60px}
  section{margin:36px 0;opacity:0;transform:translateY(30px);transition:opacity .7s,transform .7s}
  section.visible{opacity:1;transform:none}
  .card{background:var(--glass);border:1px solid var(--border);border-radius:var(--radius);padding:28px;backdrop-filter:blur(12px);box-shadow:0 20px 40px rgba(0,0,0,.25)}
  h2{font-size:1.6rem;margin-bottom:14px}
  h3{margin:14px 0 6px}
  p{margin-bottom:12px}
  .muted{color:var(--muted)}
  .hero{text-align:center;padding:60px 20px 40px}
  .hero h1{font-size:clamp(2rem,6vw,3.4rem);line-height:1.15;background:var(--grad);background-size:200% 200%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:shift 6s ease infinite}
  @keyframes shift{50%{background-position:100% 50%}}
  .hero .q{max-width:720px;margin:18px auto;font-size:1.1rem;color:var(--muted)}
  .keys{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin:22px 0}
  kbd{display:inline-block;min-width:44px;text-align:center;padding:8px 14px;border-radius:10px;border:1px solid var(--border);border-bottom-width:4px;background:var(--bg2);color:var(--text);font-family:Consolas,monospace;font-weight:700}
  .keys kbd{animation:float 3s ease-in-out infinite}
  .keys kbd:nth-of-type(2){animation-delay:.3s}.keys kbd:nth-of-type(3){animation-delay:.6s}
  @keyframes float{50%{transform:translateY(-6px)}}
  kbd.s{animation:none;min-width:0;padding:2px 8px;border-bottom-width:2px}
  .plus{align-self:center;color:var(--muted)}
  .badges{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:18px}
  .badge{padding:6px 14px;border-radius:999px;font-size:.85rem;border:1px dashed var(--border);color:var(--muted);filter:grayscale(1);opacity:.6;transition:.4s}
  .badge.on{filter:none;opacity:1;color:var(--text);border:1px solid var(--green);background:rgba(34,197,94,.15);transform:scale(1.05)}
  .note{border-left:4px solid var(--amber);background:rgba(245,158,11,.12);padding:12px 16px;border-radius:12px;margin:14px 0}
  .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}
  .mini{background:var(--glass);border:1px solid var(--border);border-radius:16px;padding:16px;transition:transform .25s}
  .mini:hover{transform:translateY(-5px) rotate(-.5deg)}
  .tip{border-bottom:1px dotted var(--blue);cursor:help;position:relative;color:var(--blue)}
  .tip::after{content:attr(data-tip);position:absolute;left:50%;bottom:130%;transform:translateX(-50%) scale(.9);width:220px;padding:8px 10px;border-radius:10px;background:#000;color:#fff;font-size:.8rem;line-height:1.4;opacity:0;pointer-events:none;transition:.2s;z-index:10}
  .tip:hover::after,.tip:focus::after{opacity:1;transform:translateX(-50%) scale(1)}
  ol.steps{list-style:none;counter-reset:s}
  ol.steps li{counter-increment:s;position:relative;padding:12px 12px 12px 56px;margin-bottom:10px;background:var(--glass);border:1px solid var(--border);border-radius:14px}
  ol.steps li::before{content:counter(s);position:absolute;left:12px;top:50%;transform:translateY(-50%);width:32px;height:32px;border-radius:50%;background:var(--grad);display:grid;place-items:center;font-weight:700;color:#fff}
  /* demo editor */
  .editor{background:#1e1e2e;border:1px solid var(--border);border-radius:14px;padding:10px 0;font-family:Consolas,monospace;font-size:.95rem;color:#e6e8f5;user-select:none;-webkit-user-select:none;touch-action:none;overflow-x:auto;cursor:crosshair}
  .row{display:flex;padding:1px 14px;white-space:pre;min-width:max-content}
  .row .n{display:inline-block;width:34px;color:#6b7094}
  .ch{display:inline-block;width:1ch;height:1.5em}
  .ch.sel{background:rgba(139,92,246,.55)}
  .ch.box{background:rgba(34,197,94,.55)}
  .controls{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}
  .status{margin-top:10px;min-height:1.6em;color:var(--green);font-weight:600}
  .tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
  .tab{border:1px solid var(--border);background:var(--glass);color:var(--text);padding:8px 16px;border-radius:999px;cursor:pointer}
  .tab[aria-selected="true"]{background:var(--grad);color:#fff;border-color:transparent}
  .panel{display:none}.panel.active{display:block;animation:fade .4s}
  @keyframes fade{from{opacity:0;transform:translateY(8px)}}
  .code{position:relative;background:#14141f;border-radius:14px;padding:18px;font-family:Consolas,monospace;font-size:.9rem;overflow-x:auto;color:#e6e8f5;margin:10px 0;white-space:pre}
  .code .k{color:#c4a5ff}.code .s{color:#86efac}.code .c{color:#7c82a8}.code .f{color:#7cb7ff}.code .h{background:rgba(34,197,94,.4);border-radius:3px}
  .copy{position:absolute;top:8px;right:8px;padding:5px 10px;font-size:.75rem;border-radius:8px;border:1px solid var(--border);background:var(--glass);color:var(--text);cursor:pointer}
  .acc{border:1px solid var(--border);border-radius:14px;margin-bottom:10px;overflow:hidden;background:var(--glass)}
  .acc-head{width:100%;text-align:left;background:none;border:none;color:var(--text);padding:14px 18px;font-size:1rem;font-weight:600;cursor:pointer;display:flex;justify-content:space-between}
  .acc-body{max-height:0;overflow:hidden;transition:max-height .4s ease;padding:0 18px}
  .acc.open .acc-body{max-height:300px;padding:0 18px 14px}
  .chev{transition:.3s}.acc.open .chev{transform:rotate(180deg)}
  .bad{color:var(--red);font-weight:700}.good{color:var(--green);font-weight:700}
  .opt{display:block;width:100%;text-align:left;margin:8px 0;padding:12px 16px;border-radius:12px;border:1px solid var(--border);background:var(--glass);color:var(--text);cursor:pointer;transition:.2s;font-size:1rem}
  .opt:hover:not(:disabled){border-color:var(--purple);transform:translateX(4px)}
  .opt.right{background:rgba(34,197,94,.25);border-color:var(--green)}
  .opt.wrong{background:rgba(239,68,68,.25);border-color:var(--red)}
  .qbar{height:8px;background:var(--glass);border-radius:99px;overflow:hidden;margin-bottom:14px}
  .qbar div{height:100%;width:0;background:var(--grad);transition:width .4s}
  .feedback{min-height:1.6em;font-weight:600;margin-top:6px}
  table{width:100%;border-collapse:collapse}
  td,th{padding:10px;border-bottom:1px solid var(--border);text-align:left}
  footer{text-align:center;padding:30px 16px;color:var(--muted);border-top:1px solid var(--border)}
  canvas#confetti{position:fixed;inset:0;pointer-events:none;z-index:200}
  @media(max-width:600px){.card{padding:18px}}
  @media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}section{opacity:1;transform:none}}
</style>
</head>
<body>
<div id="progress" role="progressbar" aria-label="Page scroll progress"></div>
<canvas id="confetti" aria-hidden="true"></canvas>

<nav aria-label="Tutorial sections">
  <a href="#what">What</a><a href="#why">Why</a><a href="#how">How</a><a href="#demo">Demo</a>
  <a href="#code">Code</a><a href="#mistakes">Mistakes</a><a href="#tips">Tips</a><a href="#quiz">Quiz</a><a href="#summary">Cheat Sheet</a>
  <span class="spacer"></span>
  <button class="btn ghost" id="themeBtn" aria-label="Toggle light and dark theme">๐ŸŒ—</button>
</nav>

<header class="hero">
  <h1>๐Ÿ–ฑ๏ธ Select Many Lines with One Drag</h1>
  <p class="q">โ€œWhat keyboard shortcut combined with a mouse drag allows you to select multiple lines at once in Visual Studio?โ€</p>
  <div class="keys" aria-label="Shift plus Alt plus mouse drag">
    <kbd>Shift</kbd><span class="plus">+</span><kbd>Alt</kbd><span class="plus">+</span><kbd>๐Ÿ–ฑ๏ธ Drag</kbd>
  </div>
  <p class="muted">๐ŸŽฎ Collect all 5 badges. Let's go, player one!</p>
  <div class="badges">
    <span class="badge" id="b-read">๐Ÿ“– Reader</span>
    <span class="badge" id="b-demo">๐Ÿ•น๏ธ Box Selector</span>
    <span class="badge" id="b-copy">๐Ÿ“‹ Copy Cat</span>
    <span class="badge" id="b-quiz">๐Ÿง  Quiz Master</span>
    <span class="badge" id="b-tip">๐Ÿ’ก Tip Explorer</span>
  </div>
</header>

<main>
  <section id="what"><div class="card">
    <h2>๐Ÿค” What Is It?</h2>
    <p>Picture a spreadsheet hidden inside your code. Sometimes you only want one <em>column</em> of it, like the first word on each of 10 lines. Normal dragging selects text like a highlighter on a page: it flows from the start to the end. That won't work here.</p>
    <p>Hold <strong>Shift + Alt</strong> and drag with the mouse. The editor then selects a <span class="tip" tabindex="0" data-tip="Also called column selection. It highlights a rectangle of text instead of a continuous stream.">box (rectangle)</span> that covers several lines at once. Anything you type or delete now happens on <strong>every selected line</strong> at the same time. โœจ</p>
    <div class="note">โš ๏ธ <strong>Heads-up:</strong> Editors differ. In <strong>Visual Studio Code</strong>, <kbd class="s">Shift</kbd>+<kbd class="s">Alt</kbd>+drag is box selection. In full <strong>Visual Studio</strong>, <kbd class="s">Alt</kbd>+drag also makes a box selection, and <kbd class="s">Shift</kbd>+<kbd class="s">Alt</kbd>+arrow keys extend it from the keyboard. On macOS, use <kbd class="s">Shift</kbd>+<kbd class="s">Option</kbd>.</div>
  </div></section>

  <section id="why"><div class="card">
    <h2>๐Ÿš€ Why Use It?</h2>
    <div class="grid">
      <div class="mini">โœ๏ธ <strong>Edit many lines at once</strong><p class="muted">Type once and it appears on every selected line.</p></div>
      <div class="mini">๐Ÿงน <strong>Delete a column</strong><p class="muted">Remove unwanted prefixes or numbers in one go.</p></div>
      <div class="mini">๐Ÿงฑ <strong>Align and format</strong><p class="muted">Add the same text at the same position on each line.</p></div>
      <div class="mini">โฑ๏ธ <strong>Save time</strong><p class="muted">No more repeating the same edit line by line.</p></div>
    </div>
  </section>

  <section id="how"><div class="card">
    <h2>๐Ÿ› ๏ธ How Does It Work?</h2>
    <ol class="steps">
      <li>Place the mouse at the start of the first spot you want to select.</li>
      <li>Press and hold <kbd class="s">Shift</kbd> + <kbd class="s">Alt</kbd>.</li>
      <li>While holding the keys, <strong>drag the cursor</strong> down (or up) across the lines you want.</li>
      <li>Release the mouse. A highlighted box covers all those lines. ๐ŸŽ‰</li>
      <li>Type, paste or delete. The change applies to every line in the box.</li>
    </ol>
  </div></section>

  <section id="demo"><div class="card">
    <h2>๐Ÿ•น๏ธ Live Demo</h2>
    <p>Hold <kbd class="s">Shift</kbd>+<kbd class="s">Alt</kbd> and drag across the code below. <span class="muted">(Green = box selection. Purple = normal selection when you drag without the keys.)</span></p>
    <div class="editor" id="editor" role="application" aria-label="Mini code editor. Hold Shift and Alt while dragging the mouse to make a box selection."></div>
    <div class="controls">
      <button class="btn ghost" id="simBtn" aria-pressed="false">๐Ÿ“ฑ Simulate Shift+Alt (for touch)</button>
      <button class="btn ghost" id="clear">โ†บ Clear selection</button>
    </div>
    <div class="status" id="status" aria-live="polite"></div>
  </div></section>

  <section id="code"><div class="card">
    <h2>๐Ÿ” Code Breakdown</h2>
    <div class="tabs" role="tablist">
      <button class="tab" role="tab" aria-selected="true" data-p="p1">Normal drag</button>
      <button class="tab" role="tab" aria-selected="false" data-p="p2">Box drag</button>
      <button class="tab" role="tab" aria-selected="false" data-p="p3">Key parts</button>
    </div>
    <div class="panel active" id="p1">
      <p class="muted">Normal drag selects a continuous stream of text.</p>
      <div class="code"><button class="copy" data-copy="name = 'Ann'&#10;age = 21&#10;city = 'Rome'">Copy</button><span class="k">name</span> = <span class="s">'Ann'</span>
<span class="k">age</span> = <span class="s">21</span>
<span class="k">city</span> = <span class="s">'Rome'</span>   <span class="c">// selection flows line to line</span></div>
    </div>
    <div class="panel" id="p2">
      <p class="muted">Box drag selects the same columns on every line.</p>
      <div class="code"><button class="copy" data-copy="name = 'Ann'&#10;age = 21&#10;city = 'Rome'">Copy</button><span class="h">name</span> = <span class="s">'Ann'</span>
<span class="h">age </span> = <span class="s">21</span>
<span class="h">city</span> = <span class="s">'Rome'</span>   <span class="c">// type "user_" and all 3 lines change</span></div>
    </div>
    <div class="panel" id="p3">
      <div class="code"><button class="copy" data-copy="Shift + Alt + drag the mouse">Copy</button><span class="f">Shift</span>  <span class="c">โ†’ modifier key #1</span>
<span class="f">Alt</span>    <span class="c">โ†’ modifier key #2 (Option on Mac)</span>
<span class="f">Drag</span>   <span class="c">โ†’ press mouse button and move across the lines</span></div>
    </div>
  </div></section>

  <section id="mistakes"><div class="card">
    <h2>๐Ÿšง Common Mistakes</h2>
    <p class="muted">Tap each one to open it.</p>
    <div class="acc"><button class="acc-head" aria-expanded="false"><span>โŒ Releasing the keys too early</span><span class="chev">โ–พ</span></button><div class="acc-body"><p><span class="bad">Wrong:</span> Letting go of Shift/Alt in the middle of the drag.<br><span class="good">Right:</span> Hold both keys until you release the mouse.</p></div></div>
    <div class="acc"><button class="acc-head" aria-expanded="false"><span>โŒ Expecting a normal selection</span><span class="chev">โ–พ</span></button><div class="acc-body"><p>Without the keys, dragging selects a continuous stream. You only get a box with the modifiers.</p></div></div>
    <div class="acc"><button class="acc-head" aria-expanded="false"><span>โŒ Using it on short, uneven lines</span><span class="chev">โ–พ</span></button><div class="acc-body"><p>If some lines are shorter than the box, they may not be fully covered. Check the highlight before you type.</p></div></div>
    <div class="acc"><button class="acc-head" aria-expanded="false"><span>โŒ Assuming every editor is the same</span><span class="chev">โ–พ</span></button><div class="acc-body"><p>Some editors use Alt-only or Ctrl+Alt for related features, such as adding multiple cursors. Check your keybindings if the box doesn't appear.</p></div></div>
  </div></section>

  <section id="tips"><div class="card">
    <h2>๐Ÿ’Ž Pro Tips</h2>
    <div class="grid" id="tipGrid">
      <button class="mini opt" data-t="Zero-width box: click at one column and drag straight down to get a multi-line cursor, then just type.">๐Ÿ“ Multi-line cursor</button>
      <button class="mini opt" data-t="Use Shift + Alt + arrow keys to grow the box without touching the mouse.">โŒจ๏ธ Keyboard growth</button>
      <button class="mini opt" data-t="Press Esc to leave box selection and go back to a single cursor.">๐Ÿšช Esc to exit</button>
      <button class="mini opt" data-t="Copy a box, then paste it. Many editors paste each line of the box in the matching place.">๐Ÿ“‹ Copy a column</button>
    </div>
    <p class="status" id="tipOut" aria-live="polite" style="color:var(--blue)">๐Ÿ‘† Tap a tip to reveal it.</p>
  </div></section>

  <section id="quiz"><div class="card">
    <h2>๐Ÿง  Practice Quiz</h2>
    <div class="qbar"><div id="qb"></div></div>
    <div id="qbox"></div>
  </div></section>

  <section id="summary"><div class="card">
    <h2>๐Ÿ“Œ Summary Card</h2>
    <table>
      <tr><th>Action</th><th>Shortcut</th></tr>
      <tr><td>Box-select multiple lines</td><td><kbd class="s">Shift</kbd> + <kbd class="s">Alt</kbd> + drag</td></tr>
      <tr><td>Grow box by keyboard</td><td><kbd class="s">Shift</kbd> + <kbd class="s">Alt</kbd> + arrows</td></tr>
      <tr><td>Mac</td><td>Shift + Option + drag</td></tr>
      <tr><td>Exit box selection</td><td><kbd class="s">Esc</kbd></td></tr>
    </table>
    <div class="code" style="margin-top:14px"><button class="copy" data-copy="shift + alt + &quot;Select lines&quot;">Copy</button>shift + alt + "Select lines"</div>
  </div></section>
</main>

<footer>Made with ๐Ÿ’œ ยท Generated by <strong>AI Prompt Dictionary</strong></footer>

<script>
(function(){
  var $=function(s){return document.querySelector(s)}, $$=function(s){return Array.prototype.slice.call(document.querySelectorAll(s))};
  var earned={};
  function award(id){ if(earned[id])return; earned[id]=true; $('#b-'+id).classList.add('on'); }

  window.addEventListener('scroll',function(){
    var h=document.documentElement,p=h.scrollTop/(h.scrollHeight-h.clientHeight)*100;
    $('#progress').style.width=p+'%'; $('#progress').setAttribute('aria-valuenow',Math.round(p));
    if(p>92) award('read');
  });
  var io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting)e.target.classList.add('visible')})},{threshold:.1});
  $$('section').forEach(function(s){io.observe(s)});
  $('#themeBtn').onclick=function(){document.body.classList.toggle('light')};

  /* demo */
  var code=["name  = 'Ann'","age   = 21","city  = 'Rome'","job   = 'Chef'","pet   = 'Cat'"];
  var ed=$('#editor'), sim=false, start=null, drag=false, box=false;
  function esc(t){return t.replace(/&/g,'&amp;').replace(/</g,'&lt;')}
  ed.innerHTML=code.map(function(t,r){
    var cells='';
    for(var c=0;c<20;c++){cells+='<span class="ch" data-r="'+r+'" data-c="'+c+'">'+(c<t.length?esc(t[c]):' ')+'</span>';}
    return '<div class="row"><span class="n">'+(r+1)+'</span>'+cells+'</div>';
  }).join('');
  var cells=$$('#editor .ch');
  function clearSel(){cells.forEach(function(x){x.classList.remove('sel','box')})}
  function paint(a,b,isBox){
    clearSel();
    var r1=Math.min(a.r,b.r), r2=Math.max(a.r,b.r);
    cells.forEach(function(x){
      var r=+x.dataset.r,c=+x.dataset.c,on=false;
      if(isBox){ on = c>=Math.min(a.c,b.c) && c<=Math.max(a.c,b.c) && r>=r1 && r<=r2; }
      else{
        var s=(a.r<b.r||(a.r===b.r&&a.c<=b.c))?a:b, e=(s===a)?b:a;
        on = (r>s.r||(r===s.r&&c>=s.c)) && (r<e.r||(r===e.r&&c<=e.c));
      }
      if(on) x.classList.add(isBox?'box':'sel');
    });
  }
  function pos(e){
    var el=document.elementFromPoint(e.clientX,e.clientY);
    if(el&&el.classList.contains('ch')) return {r:+el.dataset.r,c:+el.dataset.c};
    return null;
  }
  ed.addEventListener('pointerdown',function(e){
    var p=pos(e); if(!p)return;
    drag=true; start=p; box=(e.shiftKey&&e.altKey)||sim;
    paint(p,p,box); e.preventDefault();
  });
  window.addEventListener('pointermove',function(e){
    if(!drag)return;
    var p=pos(e); if(p) paint(start,p,box);
  });
  window.addEventListener('pointerup',function(e){
    if(!drag)return; drag=false;
    var p=pos(e)||start, lines=Math.abs(p.r-start.r)+1;
    if(box&&lines>=2){
      $('#status').textContent='โœ… Box selection across '+lines+' lines! Now typing would edit all of them.'; award('demo');
    } else if(box){
      $('#status').textContent='Drag across at least 2 lines to see the box power. โ†•๏ธ';
    } else {
      $('#status').textContent='That was a normal selection (purple). Hold Shift + Alt and try again! ๐Ÿ˜‰';
    }
  });
  $('#clear').onclick=function(){clearSel();$('#status').textContent='Cleared. Try a box drag!'};
  $('#simBtn').onclick=function(){
    sim=!sim; this.classList.toggle('on',sim); this.setAttribute('aria-pressed',sim);
    this.textContent=sim?'๐Ÿ“ฑ Shift+Alt simulated: ON':'๐Ÿ“ฑ Simulate Shift+Alt (for touch)';
  };
  $('#status').textContent='Hold Shift + Alt and drag across the lines above.';

  /* tabs */
  $$('.tab').forEach(function(t){t.onclick=function(){
    $$('.tab').forEach(function(x){x.setAttribute('aria-selected','false')});
    t.setAttribute('aria-selected','true');
    $$('.panel').forEach(function(p){p.classList.remove('active')});
    $('#'+t.dataset.p).classList.add('active');
  }});

  /* copy */
  $$('.copy').forEach(function(b){b.onclick=function(){
    var txt=b.dataset.copy;
    function ok(){b.textContent='Copied! โœ”';award('copy');setTimeout(function(){b.textContent='Copy'},1500)}
    if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(txt).then(ok)}
    else{var ta=document.createElement('textarea');ta.value=txt;document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove();ok()}
  }});

  /* accordion */
  $$('.acc-head').forEach(function(h){h.onclick=function(){
    var a=h.parentNode,o=a.classList.toggle('open'); h.setAttribute('aria-expanded',o);
  }});

  /* tips */
  var seen={};
  $$('#tipGrid button').forEach(function(b){b.onclick=function(){
    $('#tipOut').textContent='๐Ÿ’ก '+b.dataset.t; seen[b.dataset.t]=1;
    if(Object.keys(seen).length>=3) award('tip');
  }});

  /* quiz */
  var Q=[
    {q:'Which combination plus a mouse drag selects multiple lines as a box?',o:['Ctrl + Z','Shift + Alt','Ctrl + S','Tab + Enter'],a:1},
    {q:'What happens when you type while a box selection is active?',o:['Only the first line changes','Nothing happens','Every selected line changes at once'],a:2},
    {q:'What shape does the selection have with Shift + Alt + drag?',o:['A rectangle (box)','A single word','A random shape'],a:0}
  ];
  var qi=0,score=0;
  function showQ(){
    var box=$('#qbox'); $('#qb').style.width=(qi/Q.length*100)+'%';
    if(qi>=Q.length){
      box.innerHTML='<h3>๐Ÿ† Score: '+score+' / '+Q.length+'</h3><p>'+(score===Q.length?'Perfect! You are a selection wizard! ๐Ÿง™':'Nice try! Review the sections above and play again. ๐Ÿ’ช')+'</p><button class="btn" id="again">Play again</button>';
      $('#again').onclick=function(){qi=0;score=0;showQ()};
      if(score===Q.length) award('quiz');
      return;
    }
    var q=Q[qi];
    box.innerHTML='<h3>Question '+(qi+1)+' of '+Q.length+'</h3><p>'+q.q+'</p>'+q.o.map(function(t,i){return '<button class="opt" data-i="'+i+'">'+t+'</button>'}).join('')+'<div class="feedback" aria-live="polite"></div>';
    $$('#qbox .opt').forEach(function(b){b.onclick=function(){
      var i=+b.dataset.i,fb=$('#qbox .feedback');
      $$('#qbox .opt').forEach(function(x){x.disabled=true});
      if(i===q.a){b.classList.add('right');fb.textContent='๐ŸŽ‰ Correct!';score++;boom();}
      else{b.classList.add('wrong');$$('#qbox .opt')[q.a].classList.add('right');fb.textContent='Not quite. The right answer is highlighted. ๐Ÿ’ก';}
      setTimeout(function(){qi++;showQ()},1400);
    }});
  }
  showQ();

  /* confetti */
  var cv=$('#confetti'),cx=cv.getContext('2d'),parts=[],raf;
  function size(){cv.width=innerWidth;cv.height=innerHeight}
  size(); addEventListener('resize',size);
  function boom(){
    var cols=['#8b5cf6','#3b82f6','#22c55e','#f59e0b','#ef4444'];
    for(var i=0;i<90;i++) parts.push({x:innerWidth/2,y:innerHeight/2,vx:(Math.random()-.5)*14,vy:Math.random()*-12-2,c:cols[i%5],s:Math.random()*7+3,l:100});
    if(!raf) loop();
  }
  function loop(){
    cx.clearRect(0,0,cv.width,cv.height);
    parts=parts.filter(function(p){return p.l>0});
    parts.forEach(function(p){p.x+=p.vx;p.y+=p.vy;p.vy+=.35;p.l--;cx.fillStyle=p.c;cx.fillRect(p.x,p.y,p.s,p.s)});
    if(parts.length) raf=requestAnimationFrame(loop); else {raf=null;cx.clearRect(0,0,cv.width,cv.height)}
  }
})();
</script>
</body>
</html>
Live Preview