diff --git a/.vnurc b/.vnurc
index db2d5aa3f5..904e2b308b 100644
--- a/.vnurc
+++ b/.vnurc
@@ -8,5 +8,7 @@ An “img” element must have an “alt” attribute, except under certain cond
# Ignoring aria-posinset and aria-setsize on role row
Attribute “aria-posinset” not allowed on element “tr” at this point.
Attribute “aria-setsize” not allowed on element “tr” at this point.
+# Ignoring role meter
+Bad value “meter” for attribute “role” on element “div”.
# Deleted Section Archive
The “longdesc” attribute on the “img” element is obsolete. Use a regular “a” element to link to the description.
diff --git a/examples/meter/css/meter.css b/examples/meter/css/meter.css
new file mode 100644
index 0000000000..75934ca1e8
--- /dev/null
+++ b/examples/meter/css/meter.css
@@ -0,0 +1,17 @@
+
+[role=meter] {
+ padding: 2px;
+ width: 200px;
+ height: 40px;
+ border: 2px solid black;
+ border-radius: 5px;
+}
+
+.fill {
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+ border: 2px solid black;
+ border-radius: 3px;
+ background-color: black;
+}
diff --git a/examples/meter/js/meter.js b/examples/meter/js/meter.js
new file mode 100644
index 0000000000..440647584a
--- /dev/null
+++ b/examples/meter/js/meter.js
@@ -0,0 +1,117 @@
+var Meter = function (element) {
+ this.rootEl = element;
+ this.fillEl = element.querySelector('.fill');
+
+ // set up min, max, and current value
+ var min = element.getAttribute('aria-valuemin');
+ var max = element.getAttribute('aria-valuemax');
+ var value = element.getAttribute('aria-valuenow');
+ this._update(parseFloat(min), parseFloat(max), parseFloat(value));
+};
+
+/* Private methods */
+
+// returns a number representing a percentage between 0 - 100
+Meter.prototype._calculatePercentFill = function (min, max, value) {
+ if (typeof min !== 'number' || typeof max !== 'number' || typeof value !== 'number') {
+ return 0;
+ }
+
+ return 100 * (value - min) / (max - min);
+};
+
+// returns an hsl color string between red and green
+Meter.prototype._getColorValue = function (percent) {
+ // red is 0deg, green is 120deg in hsl
+ // if percent is 100, hue should be red, and if percent is 0, hue should be green
+ var hue = (percent / 100) * (0 - 120) + 120;
+
+ return 'hsl(' + hue + ', 100%, 40%)';
+};
+
+// no return value; updates the meter element
+Meter.prototype._update = function (min, max, value) {
+ // update fill width
+ if (min !== this.min || max !== this.max || value !== this.value) {
+ var percentFill = this._calculatePercentFill(min, max, value);
+ this.fillEl.style.width = percentFill + '%';
+ this.fillEl.style.color = this._getColorValue(percentFill);
+ }
+
+ // update aria attributes
+ if (min !== this.min) {
+ this.min = min;
+ this.rootEl.setAttribute('aria-valuemin', min + '');
+ }
+
+ if (max !== this.max) {
+ this.max = max;
+ this.rootEl.setAttribute('aria-valuemax', max + '');
+ }
+
+ if (value !== this.value) {
+ this.value = value;
+ this.rootEl.setAttribute('aria-valuenow', value + '');
+ }
+};
+
+/* Public methods */
+
+// no return value; modifies the meter element based on a new value
+Meter.prototype.setValue = function (value) {
+ if (typeof value !== 'number') {
+ value = parseFloat(value);
+ }
+
+ if (!isNaN(value)) {
+ this._update(this.min, this.max, value);
+ }
+};
+
+/* Code for example page */
+
+window.addEventListener('load', function () {
+ // helper function to randomize example meter value
+ function getRandomValue (min, max) {
+ var range = max - min;
+ return Math.floor((Math.random() * range) + min);
+ }
+
+ // init meters
+ var meterEls = document.querySelectorAll('[role=meter]');
+ var meters = [];
+ Array.prototype.slice.call(meterEls).forEach(function (meterEl) {
+ meters.push(new Meter(meterEl));
+ });
+
+ // randomly update meter values
+
+ // returns an id for setInterval
+ function playMeters () {
+ return window.setInterval(function () {
+ meters.forEach(function (meter) {
+ meter.setValue(Math.random() * 100);
+ });
+ }, 5000);
+ }
+
+ // start meters
+ var updateInterval = playMeters();
+
+ // play/pause meter updates
+ var playButton = document.querySelector('.play-meters');
+ playButton.addEventListener('click', function () {
+ var isPaused = playButton.classList.contains('paused');
+
+ if (isPaused) {
+ updateInterval = playMeters();
+ playButton.classList.remove('paused');
+ playButton.innerHTML = 'Pause Updates';
+ }
+ else {
+ clearInterval(updateInterval);
+ playButton.classList.add('paused');
+ playButton.innerHTML = 'Start Updates';
+ }
+ });
+});
diff --git a/examples/meter/meter.html b/examples/meter/meter.html
new file mode 100644
index 0000000000..6ad91436a6
--- /dev/null
+++ b/examples/meter/meter.html
@@ -0,0 +1,142 @@
+
+
+