Migrate example to pkg:web, update minimum required Dart version (dart-lang/markdown#582)

diff --git a/pkgs/markdown/.github/workflows/test-package.yml b/pkgs/markdown/.github/workflows/test-package.yml
index 4301790..80d6c1d 100644
--- a/pkgs/markdown/.github/workflows/test-package.yml
+++ b/pkgs/markdown/.github/workflows/test-package.yml
@@ -47,7 +47,7 @@
       matrix:
         # Add macos-latest and/or windows-latest if relevant for this package.
         os: [ubuntu-latest]
-        sdk: [3.1, dev]
+        sdk: [3.2, dev]
     steps:
       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
       - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index 42073b7..55760bf 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 7.2.2-wip
+
+* Require Dart `^3.2.0`.
+
 ## 7.2.1
 
 * Address a termination issue with GitHub alert syntax parsing.
diff --git a/pkgs/markdown/example/app.dart b/pkgs/markdown/example/app.dart
index d8ea6e7..925d4f0 100644
--- a/pkgs/markdown/example/app.dart
+++ b/pkgs/markdown/example/app.dart
@@ -3,17 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:html';
+import 'dart:js_interop';
 
 import 'package:markdown/markdown.dart' as md;
+import 'package:web/web.dart';
 
 import 'highlight.dart';
 
-final markdownInput = querySelector('#markdown') as TextAreaElement;
-final htmlDiv = querySelector('#html') as DivElement;
-final versionSpan = querySelector('.version') as SpanElement;
+final markdownInput =
+    document.querySelector('#markdown') as HTMLTextAreaElement;
+final htmlDiv = document.querySelector('#html') as HTMLDivElement;
+final versionSpan = document.querySelector('.version') as HTMLSpanElement;
 
-final nullSanitizer = NullTreeSanitizer();
 const typing = Duration(milliseconds: 150);
 const introText = '''Markdown is the **best**!
 
@@ -26,9 +27,10 @@
 * ...and _so much more_...''';
 
 // Flavor support.
-final basicRadio = querySelector('#basic-radio') as HtmlElement;
-final commonmarkRadio = querySelector('#commonmark-radio') as HtmlElement;
-final gfmRadio = querySelector('#gfm-radio') as HtmlElement;
+final basicRadio = document.querySelector('#basic-radio') as HTMLElement;
+final commonmarkRadio =
+    document.querySelector('#commonmark-radio') as HTMLElement;
+final gfmRadio = document.querySelector('#gfm-radio') as HTMLElement;
 md.ExtensionSet? extensionSet;
 
 final extensionSets = {
@@ -54,7 +56,7 @@
   }
 
   // GitHub is the default extension set.
-  gfmRadio.attributes['checked'] = '';
+  gfmRadio.attributes.getNamedItem('checked')?.value = '';
   gfmRadio.querySelector('.glyph')!.text = 'radio_button_checked';
   extensionSet = extensionSets[gfmRadio.id];
   _renderMarkdown();
@@ -65,19 +67,16 @@
 }
 
 void _renderMarkdown([Event? event]) {
-  final markdown = markdownInput.value!;
+  final markdown = markdownInput.value;
 
-  htmlDiv.setInnerHtml(
-    md.markdownToHtml(markdown, extensionSet: extensionSet),
-    treeSanitizer: nullSanitizer,
-  );
+  htmlDiv.innerHTML = md.markdownToHtml(markdown, extensionSet: extensionSet);
 
-  for (final block in htmlDiv.querySelectorAll('pre code')) {
+  for (final block in htmlDiv.querySelectorAll('pre code').items) {
     try {
       highlightElement(block);
     } catch (e) {
-      window.console.error('Error highlighting markdown:');
-      window.console.error(e);
+      console.error('Error highlighting markdown:'.toJS);
+      console.error(e.toString().toJS);
     }
   }
 
@@ -107,29 +106,36 @@
 }
 
 void _switchFlavor(Event e) {
-  final target = e.currentTarget as HtmlElement;
-  if (!target.attributes.containsKey('checked')) {
+  final target = e.currentTarget as HTMLElement;
+  if (target.attributes.getNamedItem('checked') == null) {
     if (basicRadio != target) {
-      basicRadio.attributes.remove('checked');
+      basicRadio.attributes.safeRemove('checked');
       basicRadio.querySelector('.glyph')!.text = 'radio_button_unchecked';
     }
     if (commonmarkRadio != target) {
-      commonmarkRadio.attributes.remove('checked');
+      commonmarkRadio.attributes.safeRemove('checked');
       commonmarkRadio.querySelector('.glyph')!.text = 'radio_button_unchecked';
     }
     if (gfmRadio != target) {
-      gfmRadio.attributes.remove('checked');
+      gfmRadio.attributes.safeRemove('checked');
       gfmRadio.querySelector('.glyph')!.text = 'radio_button_unchecked';
     }
 
-    target.attributes['checked'] = '';
+    target.attributes.getNamedItem('checked')?.value = '';
     target.querySelector('.glyph')!.text = 'radio_button_checked';
     extensionSet = extensionSets[target.id];
     _renderMarkdown();
   }
 }
 
-class NullTreeSanitizer implements NodeTreeSanitizer {
-  @override
-  void sanitizeTree(Node node) {}
+extension on NodeList {
+  List<Node> get items => [
+        for (var i = 0; i < length; i++) item(i)!,
+      ];
+}
+
+extension on NamedNodeMap {
+  void safeRemove(String qualifiedName) {
+    if (getNamedItem(qualifiedName) != null) removeNamedItem(qualifiedName);
+  }
 }
diff --git a/pkgs/markdown/example/highlight.dart b/pkgs/markdown/example/highlight.dart
index 5dea80d..b54c447 100644
--- a/pkgs/markdown/example/highlight.dart
+++ b/pkgs/markdown/example/highlight.dart
@@ -3,9 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 
 @JS('hljs')
-library hljs;
+library;
 
-import 'package:js/js.dart';
+import 'dart:js_interop';
+
+import 'package:web/web.dart';
 
 @JS()
-external void highlightElement(Object block);
+external void highlightElement(Node block);
diff --git a/pkgs/markdown/lib/src/version.dart b/pkgs/markdown/lib/src/version.dart
index b7a948d..b0cf861 100644
--- a/pkgs/markdown/lib/src/version.dart
+++ b/pkgs/markdown/lib/src/version.dart
@@ -1,2 +1,2 @@
 // Generated code. Do not modify.
-const packageVersion = '7.2.1';
+const packageVersion = '7.2.2-wip';
diff --git a/pkgs/markdown/pubspec.yaml b/pkgs/markdown/pubspec.yaml
index 90dfea3..bd85b65 100644
--- a/pkgs/markdown/pubspec.yaml
+++ b/pkgs/markdown/pubspec.yaml
@@ -1,5 +1,5 @@
 name: markdown
-version: 7.2.1
+version: 7.2.2-wip
 
 description: >-
   A portable Markdown library written in Dart that can parse Markdown into HTML.
@@ -11,7 +11,7 @@
   markdown:
 
 environment:
-  sdk: ^3.1.0
+  sdk: ^3.2.0
 
 dependencies:
   args: ^2.0.0
@@ -26,9 +26,9 @@
   html: ^0.15.0
   http: ^1.0.0
   io: ^1.0.0
-  js: ^0.7.0
   path: ^1.8.0
   pool: ^1.5.1
   tar: ^1.0.3
   test: ^1.16.0
+  web: '>=0.4.2 <0.6.0'
   yaml: ^3.0.0