Repository: sveltejs/svelte (JavaScript)

Issue:
Custom element attribute reflection fails when a dash is present in the attribute
### Describe the bug

If I define an exported boolean property and configure it to be reflected back to the DOM, then it will work fine if you do not manipulate the attribute name. However, if the attribute name is modified to include a dash, then the reflection will only trigger once and then stop triggering afterward.

### Reproduction

I would expect that every time I click the "Toggle" button to hide/show the inner div, then the `my-element` custom element would likewise toggle its `aria-expanded` attribute accordingly. Instead, it will appear after the first click and then stop toggling afterwards. This problem does not occur if I set `attribute` to "expanded" or leave it undefined.

```svelte
<svelte:options customElement={{ tag: "my-element", props: {
  expanded: { reflect: true, type: 'Boolean', attribute: 'aria-expanded' }
}}} />

<script>
  export let expanded = false;
</script>

<div>
  <button on:click={() => expanded = !expanded}>Toggle</button>
  <div class:hidden={!expanded}>Hidden Text</div>
</div>

<style>
  .hidden {
    display: none;
  }
</style>
```

### Logs

_No response_

### System Info

```shell
System:
    OS: Windows 10 10.0.22000
    CPU: (20) x64 13th Gen Intel(R) Core(TM) i9-13900H
    Memory: 16.82 GB / 63.66 GB
  Binaries:
    Node: 20.2.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.19 - C:\Program Files\nodejs\yarn.CMD
    npm: 9.6.6 - C:\Program Files\nodejs\npm.CMD
    pnpm: 8.6.9 - C:\Program Files\nodejs\pnpm.CMD
  Browsers:
    Edge: Spartan (44.22000.120.0), Chromium (115.0.1901.203)
    Internet Explorer: 11.0.22000.120
  npmPackages:
    svelte: ^4.0.0 => 4.1.1
```


### Severity

annoyance


Discussion:
(Upon further investigation, it seems like the example is a bit poor since you need `aria-expanded="true"` to match accessibility concerns, not just `aria-expanded`; however, that bears no relevance to the buggy behavior itself which applies regardless)



Patch:
diff --git a/.changeset/tame-tomatoes-warn.md b/.changeset/tame-tomatoes-warn.md
new file mode 100644
index 000000000000..eafba8820867
--- /dev/null
+++ b/.changeset/tame-tomatoes-warn.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: take custom attribute name into account when reflecting property
diff --git a/packages/svelte/src/runtime/internal/Component.js b/packages/svelte/src/runtime/internal/Component.js
index bc5b117c2ff0..2bfdb6fd4899 100644
--- a/packages/svelte/src/runtime/internal/Component.js
+++ b/packages/svelte/src/runtime/internal/Component.js
@@ -283,7 +283,7 @@ if (typeof HTMLElement === 'function') {
 								'toAttribute'
 							);
 							if (attribute_value == null) {
-								this.removeAttribute(key);
+								this.removeAttribute(this.$$p_d[key].attribute || key);
 							} else {
 								this.setAttribute(this.$$p_d[key].attribute || key, attribute_value);
 							}


Test patch:
diff --git a/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes-add-remove/main.svelte b/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes-add-remove/main.svelte
new file mode 100644
index 000000000000..c5c80c16a3e4
--- /dev/null
+++ b/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes-add-remove/main.svelte
@@ -0,0 +1,23 @@
+<svelte:options
+	customElement={{
+		tag: 'custom-element',
+		props: {
+			expanded: { reflect: true, type: 'Boolean', attribute: 'aria-expanded' }
+		}
+	}}
+/>
+
+<script>
+	export let expanded = false;
+</script>
+
+<div>
+	<button on:click={() => (expanded = !expanded)}>Toggle</button>
+	<div class:hidden={!expanded}>Hidden Text</div>
+</div>
+
+<style>
+	.hidden {
+		display: none;
+	}
+</style>
diff --git a/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes-add-remove/test.js b/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes-add-remove/test.js
new file mode 100644
index 000000000000..d973039983b9
--- /dev/null
+++ b/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes-add-remove/test.js
@@ -0,0 +1,19 @@
+import * as assert from 'assert.js';
+import { tick } from 'svelte';
+import './main.svelte';
+
+export default async function (target) {
+	const element = document.createElement('custom-element');
+	target.appendChild(element);
+	await tick();
+
+	const el = target.querySelector('custom-element');
+	el.shadowRoot.querySelector('button').click();
+	await tick();
+
+	assert.equal(el.getAttribute('aria-expanded'), '');
+	el.shadowRoot.querySelector('button').click();
+	await tick();
+
+	assert.equal(el.getAttribute('aria-expanded'), null);
+}