<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sertunç Özgüneş</title>
    <description>The latest articles on DEV Community by Sertunç Özgüneş (@sertunc_ozgunes).</description>
    <link>https://dev.to/sertunc_ozgunes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4106512%2F021fba8b-1019-4ad4-8955-4e172837dcee.jpg</url>
      <title>DEV Community: Sertunç Özgüneş</title>
      <link>https://dev.to/sertunc_ozgunes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sertunc_ozgunes"/>
    <language>en</language>
    <item>
      <title>What an Embeddable Paycheck Calculator Taught Me About Iframes, Privacy, and Honest UX</title>
      <dc:creator>Sertunç Özgüneş</dc:creator>
      <pubDate>Wed, 02 Sep 2026 16:14:37 +0000</pubDate>
      <link>https://dev.to/sertunc_ozgunes/what-an-embeddable-paycheck-calculator-taught-me-about-iframes-privacy-and-honest-ux-180f</link>
      <guid>https://dev.to/sertunc_ozgunes/what-an-embeddable-paycheck-calculator-taught-me-about-iframes-privacy-and-honest-ux-180f</guid>
      <description>&lt;p&gt;Most “embed this tool” features are really small integration projects: install a script, create an account, obtain a key, add a container, and hope the host site's CSS does not win a specificity fight.&lt;/p&gt;

&lt;p&gt;For PaycheckForge, I wanted the integration to be boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://paycheckforge.com/embed/take-home-pay-calculator/"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"720"&lt;/span&gt;
  &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"border:0;border-radius:12px"&lt;/span&gt;
  &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
  &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Take-home pay calculator"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
  Powered by
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://paycheckforge.com"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noopener"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    PaycheckForge
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No JavaScript SDK. No API key. The host page owns placement; the iframe owns the calculator.&lt;/p&gt;

&lt;p&gt;That simple interface forced several useful engineering decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Treat the iframe boundary as a feature
&lt;/h2&gt;

&lt;p&gt;The widget has its own document and stylesheet. A university career center's CSS reset cannot unexpectedly restyle the calculator, and the calculator cannot leak selectors into the host page.&lt;/p&gt;

&lt;p&gt;The trade-off is sizing. Cross-origin frames cannot inspect each other's documents, so automatic height usually requires a &lt;code&gt;postMessage&lt;/code&gt; protocol. I chose fixed, tool-specific heights for the first version. A small registry keeps that choice visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;EmbedTool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;shortTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EmbedTool&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;take-home-pay-calculator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;shortTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Take-Home Pay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hourly-to-salary-calculator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;shortTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hourly to Salary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;520&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;overtime-pay-calculator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;shortTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Overtime Pay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;520&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is less clever than a resize protocol, but it has fewer moving parts and still works when third-party JavaScript is restricted.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Build a deliberately smaller document
&lt;/h2&gt;

&lt;p&gt;The normal site layout includes navigation, footer, consent controls, analytics, and advertising support. None of that belongs inside a utility frame.&lt;/p&gt;

&lt;p&gt;The Astro embed route renders only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the calculator,&lt;/li&gt;
&lt;li&gt;an accessible page title,&lt;/li&gt;
&lt;li&gt;one compact stylesheet, and&lt;/li&gt;
&lt;li&gt;a visible PaycheckForge attribution link.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frame uses &lt;code&gt;noindex, follow&lt;/code&gt;: search engines should index the useful canonical tool page, not a duplicate presentation shell designed for another website.&lt;/p&gt;

&lt;p&gt;The standalone route also skips PaycheckForge advertising and analytics scripts. More importantly, salary, filing-status, deduction, and result values are computed in the browser. They are not serialized into the URL or sent to PaycheckForge as part of the calculation.&lt;/p&gt;

&lt;p&gt;That is a narrow privacy promise, and narrow promises are easier to keep. I do not describe the frame as “collecting no data whatsoever,” because loading any web page still makes a normal request to a host.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Accessibility belongs in the copy-paste snippet
&lt;/h2&gt;

&lt;p&gt;An iframe without a &lt;code&gt;title&lt;/code&gt; is a mystery to a screen-reader user. The snippet therefore includes a descriptive title by default.&lt;/p&gt;

&lt;p&gt;Inside the frame, labels remain programmatically associated with fields, keyboard focus is visible, validation does not depend on color alone, and result updates use semantic text. The widget also respects reduced-motion preferences.&lt;/p&gt;

&lt;p&gt;The host site still has responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep enough vertical space for the selected tool,&lt;/li&gt;
&lt;li&gt;do not hide focus outlines,&lt;/li&gt;
&lt;li&gt;place the calculator under a meaningful heading, and&lt;/li&gt;
&lt;li&gt;provide context explaining that the result is an estimate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Embedding is also a response-header problem
&lt;/h2&gt;

&lt;p&gt;Perfect iframe markup cannot override the framed site's headers.&lt;/p&gt;

&lt;p&gt;If the widget response sends this header, external embedding will fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;X-Frame-Options: SAMEORIGIN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Likewise, an enforced CSP with &lt;code&gt;frame-ancestors 'self'&lt;/code&gt; blocks third-party parents. The safe pattern is route-specific: keep framing protection on normal account/content pages, while intentionally allowing the public &lt;code&gt;/embed/&lt;/code&gt; surface.&lt;/p&gt;

&lt;p&gt;The publisher may also have a CSP. Their &lt;code&gt;frame-src&lt;/code&gt; directive must allow &lt;code&gt;https://paycheckforge.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is worth an actual cross-origin test. Testing the iframe from the same domain can give a false sense of success.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Financial UX needs an accuracy boundary
&lt;/h2&gt;

&lt;p&gt;A paycheck estimate can look authoritative simply because it contains precise dollar amounts. Precision is not the same as certainty.&lt;/p&gt;

&lt;p&gt;The calculator therefore explains that it estimates annual federal, FICA, and state liability and spreads that estimate across pay periods. Employer withholding can differ because of Publication 15-T methods, W-4 details, year-to-date wage limits, local taxes, and state-specific payroll programs.&lt;/p&gt;

&lt;p&gt;For a financial tool, the limitations link is part of the product, not legal text to hide in a footer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this kind of widget helps
&lt;/h2&gt;

&lt;p&gt;The most natural uses are educational:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;career centers teaching students how to evaluate a first job offer,&lt;/li&gt;
&lt;li&gt;financial-wellness programs explaining gross versus take-home pay,&lt;/li&gt;
&lt;li&gt;HR onboarding pages explaining common paycheck deductions, and&lt;/li&gt;
&lt;li&gt;personal-finance lessons that compare salary, benefits, and location.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I prepared a short “First Paycheck” classroom activity around the widget so an educator can use it as a decision exercise rather than presenting a calculator with no context.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping an iframe tool, I now check five things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can it load and complete its core task from a genuinely different origin?&lt;/li&gt;
&lt;li&gt;Does the default snippet include &lt;code&gt;loading="lazy"&lt;/code&gt; and a useful &lt;code&gt;title&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Are input values kept out of URLs and analytics events?&lt;/li&gt;
&lt;li&gt;Do response headers allow only the framing behavior I intended?&lt;/li&gt;
&lt;li&gt;Does the UI explain what the result cannot guarantee?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code for an iframe can be one line. Making that line trustworthy is the real feature.&lt;/p&gt;

&lt;p&gt;You can review the available widget formats at &lt;a href="https://paycheckforge.com/embed/" rel="noopener noreferrer"&gt;PaycheckForge Embed Tools&lt;/a&gt; and the calculation boundaries on the &lt;a href="https://paycheckforge.com/about/" rel="noopener noreferrer"&gt;methodology page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have shipped an iframe widget, I would be interested in how you handled responsive height, CSP documentation, and privacy testing.&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>astro</category>
    </item>
  </channel>
</rss>
