diff --git a/src/components/status.css b/src/components/status.css
index 805ca219..1bc08122 100644
--- a/src/components/status.css
+++ b/src/components/status.css
@@ -410,6 +410,16 @@ a.card:hover {
 
 /* POLLS */
 
+.poll {
+  transition: opacity 0.2s ease-in-out;
+}
+.poll.loading {
+  opacity: 0.5;
+  pointer-events: none;
+}
+.poll.read-only {
+  pointer-events: none;
+}
 .poll-option {
   margin-top: 8px;
   padding: 8px;
diff --git a/src/components/status.jsx b/src/components/status.jsx
index 28f75ced..f0afec59 100644
--- a/src/components/status.jsx
+++ b/src/components/status.jsx
@@ -899,7 +899,11 @@ function Poll({ poll, readOnly, onUpdate = () => {} }) {
   const expiresAtDate = !!expiresAt && new Date(expiresAt);
 
   return (
-    <div class="poll">
+    <div
+      class={`poll ${readOnly ? 'read-only' : ''} ${
+        uiState === 'loading' ? 'loading' : ''
+      }`}
+    >
       {voted || expired ? (
         options.map((option, i) => {
           const { title, votesCount: optionVotesCount } = option;
@@ -959,10 +963,6 @@ function Poll({ poll, readOnly, onUpdate = () => {} }) {
             onUpdate(pollResponse);
             setUIState('default');
           }}
-          style={{
-            pointerEvents: uiState === 'loading' || readOnly ? 'none' : 'auto',
-            opacity: uiState === 'loading' ? 0.5 : 1,
-          }}
         >
           {options.map((option, i) => {
             const { title } = option;
@@ -994,6 +994,31 @@ function Poll({ poll, readOnly, onUpdate = () => {} }) {
       )}
       {!readOnly && (
         <p class="poll-meta">
+          {!expired && (
+            <>
+              <button
+                type="button"
+                class="textual"
+                disabled={uiState === 'loading'}
+                onClick={(e) => {
+                  e.preventDefault();
+                  setUIState('loading');
+                  (async () => {
+                    try {
+                      const pollResponse = await masto.poll.fetch(id);
+                      onUpdate(pollResponse);
+                    } catch (e) {
+                      // Silent fail
+                    }
+                    setUIState('default');
+                  })();
+                }}
+              >
+                Refresh
+              </button>{' '}
+              &bull;{' '}
+            </>
+          )}
           <span title={votersCount}>{shortenNumber(votersCount)}</span>{' '}
           {votersCount === 1 ? 'voter' : 'voters'}
           {votersCount !== votesCount && (
diff --git a/src/index.css b/src/index.css
index 286ebafc..b799879b 100644
--- a/src/index.css
+++ b/src/index.css
@@ -157,6 +157,15 @@ button,
   padding: 12px;
 }
 
+:is(button, .button).textual {
+  padding: 0;
+  margin: 0;
+  vertical-align: baseline;
+  color: var(--link-color);
+  background-color: transparent;
+  border-radius: 0;
+}
+
 input[type='text'],
 textarea,
 select {