In a card with a paragraph below a picture, the image ends before the card does. I extracted that relationship from the original notice-position repair and built a new module. A dedicated visual parent owns the notice while the caption stays outside, preserving which material the text describes.

The code computes supplied material kind, generation status and notice text. It does not inspect an image to discover AI provenance or factual truth. The drawing below is a new code-made synthetic diagram, not an original customer photograph. Changing caption length, visual ratio and declaration lets a reader inspect the bounded connection.

Use a parent that contains only the visual

The surface passed to attachImageNotice has one direct IMG, PICTURE or SVG child. A title or caption inside it is rejected before connection. This is an explicit structure contract for the caller, not a program that automatically repairs every existing website. Other information belongs in the outer card or caption.

Separate CSS makes that surface the positioning reference and puts the notice at a corner. It does not choose whether to fill or crop the picture. The actual source ratio and fit decision remain separate while the notice gains an anchor. A longer caption does not become part of that parent, so the anchor does not move to the end of the whole card.

Declaration checks do not establish the source facts

Input contains exactly kind, generated and disclosure. Kind is photo, document or logo, and generated is a Boolean. A declared generated photo needs nonblank notice text. Generated documents and logos are rejected under this module’s chosen policy. Non-generated material takes empty disclosure and hides the overlay.

These are checks on a supplied declaration, not on the actual file category, rights or appearance of a real space. The code does not automatically determine which file a mixed desktop/mobile picture is showing. Connect the actually selected source to its inspected declaration and call update as necessary. Source selection and provenance are not inferred from a viewport width.

Inspect position by changing the caption

The example can lengthen its caption or change the visual ratio. A declared original document hides the notice, while an attempted generated-document declaration is rejected with the previous state retained. The same synthetic drawing does not thereby become an actual generated photograph or official document. It demonstrates a declaration-to-presentation connection.

The notice input is carried through textContent. Tag-shaped text does not construct new HTML elements. Keep the visible wording concise and clear about the material’s role. An accepted maximum input length does not guarantee good reading conditions on every actual picture or very small area; inspect the size and background in the real application.

An additional check of the new module found that an accepted240-character notice rendered191px tall over a172px visual in the actual narrow page. Passing with the default text did not prove the accepted boundary. The revised code preserves the full text and input limit while bounding the notice rectangle inside the visual. Long content has a focusable native scroll area for keys such as PageDown and End.

Inspect the anchor region and notice state using a synthetic diagram and explicit declarations.

Source and reusable files

The JavaScript below is the same image_notice.mjs in the ZIP. The declared-rule function is separate from the DOM connector. update validates before changing the display, so an invalid input does not immediately erase a previously valid notice. A valid remount cleans up its old owned label and keeps one notice on the surface.

The six files contain source, CSS, Korean and English demos, Node checks and a guide. Run node checks.mjs for the included checks. The guide gives a local HTTP invocation for the module demos. This package contains neither original customer pictures nor the original template engine, and it includes no code sending values to an external server.

Explore the implementation

New independent JavaScript · declaration and visual-only notice

// New declared-metadata rule and visual-only notice connector.
const mounted = new WeakMap();

export function noticeFromDeclaration(metadata) {
  if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata) ||
      Object.keys(metadata).sort().join() !== 'disclosure,generated,kind' ||
      !['photo','document','logo'].includes(metadata.kind) ||
      typeof metadata.generated !== 'boolean' || typeof metadata.disclosure !== 'string' ||
      metadata.disclosure.length > 240) throw new TypeError('Invalid image declaration');
  if (metadata.generated && metadata.kind !== 'photo') throw new TypeError('Generated factual material is outside this module');
  const text = metadata.disclosure.trim();
  if ((metadata.generated && !text) || (!metadata.generated && text)) throw new TypeError('Declaration and notice disagree');
  return Object.freeze({kind:metadata.kind,visible:metadata.generated,text});
}

export function attachImageNotice(surface, metadata) {
  const next = noticeFromDeclaration(metadata);
  if (!surface?.children || !surface.ownerDocument || !surface.classList) throw new TypeError('Visual surface required');
  const old = mounted.get(surface);
  const children = [...surface.children].filter(node => node !== old?.element);
  if (children.length !== 1 || !['IMG','PICTURE','SVG'].includes(children[0].tagName.toUpperCase())) {
    throw new TypeError('Surface must contain only one visual, with captions outside');
  }
  old?.destroy();
  const hadClass = surface.classList.contains('image-notice-surface');
  const label = surface.ownerDocument.createElement('span');
  label.className = 'image-notice';
  label.tabIndex = 0;
  let active = true, current = next;
  const render = value => { label.textContent=value.text; label.hidden=!value.visible; };
  surface.classList.add('image-notice-surface'); surface.append(label); render(current);
  const api = Object.freeze({
    element:label,
    get notice() { return current; },
    update(value) {
      if (!active) throw new Error('Notice is detached');
      const valid = noticeFromDeclaration(value);
      current=valid; render(valid); return valid;
    },
    destroy() {
      if (!active) return;
      active=false; label.remove();
      if (!hadClass) surface.classList.remove('image-notice-surface');
      if (mounted.get(surface)===api) mounted.delete(surface);
    }
  });
  mounted.set(surface,api); return api;
}
Source, CSS, two demos, 25 checks and usage guide ZIP

Cleanup should preserve the original material

destroy removes the label owned by this connection and restores the original class membership. The visual element and its attributes remain intact. A stale object cannot remove the notice belonging to a new connection, and a detached update is rejected. Original material ownership and notice lifecycle are kept separate.

The twenty-five code checks cover invalid declaration combinations, unchanged input, visual-only structure, literal text, update, remount and cleanup with a synthetic DOM. They do not establish real-picture readability, AI provenance or original whole-site completion. Actual browser geometry and input behavior for this public example are a separate check from that isolated code result.

Checks that remain when adapting a site

Inspect the real material and choose the appropriate declaration before connecting it. When the image or responsive source changes, confirm that the declaration still matches. Examine the image/caption structure, readable notice and any important content or controls it might cover. Transparent styling is not a universal answer for every photograph.

Disclosure strings remain as text, so the module is not a customer-identity scrubber. Use generic public examples and separately decide what actual material may be disclosed. The reusable responsibility is small and inspectable: carry a previously confirmed explanation to the correct visual region, rather than pretend to infer the image’s source automatically.