Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def build_resume_markdown(payload: dict[str, Any]) -> str:
f"{CATEGORY_LABELS.get(c, c)}: {', '.join(ns)}"
for c, ns in grouped.items()
]
lines.append(field_line("技術スタック", " / ".join(parts)))
lines.append(field_line("スキルセット", " / ".join(parts)))
lines.append("")

self_pr = payload.get("self_pr", "")
Expand Down
52 changes: 29 additions & 23 deletions backend/app/services/pdf/generators/resume_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,24 @@ def _format_periods(periods: list) -> str:
return "、".join(parts)


def _format_team(project) -> str:
"""体制の人数情報を「6名(PM:1名、SE:6名)」形式の1行文字列にする。

後方互換(旧 scale → team)の正規化は shared に集約。情報が無ければ空文字を返す。
"""
team = normalize_team(project)
if not team:
return ""
total = _a(team, "total")
total_text = f"{_esc(total)}名" if total else ""
members = _a(team, "members", [])
member_strs = [
f"{_esc(_a(m, 'role'))}:{_esc(_a(m, 'count', 0))}名" for m in members if _a(m, "role")
]
member_text = f"({'、'.join(member_strs)})" if member_strs else ""
return f"{total_text}{member_text}"


def _build_project_html(project) -> str:
"""プロジェクト1件分のHTMLを組み立てる"""
# ヘッダー(3行構成: 期間/プロジェクト名、役割、工程)
Expand All @@ -71,8 +89,11 @@ def _build_project_html(project) -> str:
line1_parts.append(_esc(name))
line1 = " / ".join(line1_parts) if line1_parts else ""

# 2行目: 役割
line2 = f"役割:{_esc(role)}" if role else ""
# 2行目: 役割(体制の人数情報を「、体制:…」として役割の右側に併記する)
team_text = _format_team(project)
role_part = f"役割:{_esc(role)}" if role else ""
team_part = f"体制:{team_text}" if team_text else ""
line2 = "、".join(p for p in [role_part, team_part] if p)

# 3行目: 工程
line3 = ""
Expand All @@ -91,7 +112,7 @@ def _build_project_html(project) -> str:
left_parts.append(_md(description))
left_content = "".join(left_parts) if left_parts else "-"

# 右カラム: 開発環境(技術スタック
# 右カラム: スキルセット(技術スタックのカテゴリ別表示
stacks = _a(project, "technology_stacks", [])
grouped = group_stacks_by_category(stacks)
right_parts: list[str] = []
Expand All @@ -102,28 +123,13 @@ def _build_project_html(project) -> str:
)
right_content = "<br/>".join(right_parts) if right_parts else "-"

# 体制(後方互換: 旧 scale → team の正規化は shared に集約)
team = normalize_team(project)
team_parts: list[str] = []
if team:
total = _a(team, "total")
if total:
team_parts.append(f"{_esc(total)}名")
members = _a(team, "members", [])
member_strs = [
f"{_esc(_a(m, 'role'))}:{_a(m, 'count', 0)}" for m in members if _a(m, "role")
]
if member_strs:
team_parts.append(" / ".join(member_strs))
team_text = "<br/>".join(team_parts) if team_parts else "-"

# 体制は表のカラムではなく役割行に併記する(line2 で処理済み)。
return (
f'<div class="project">{header_html}'
f'<table class="project-table">'
f"<tr><th>業務内容</th><th>開発環境</th><th>体制</th></tr>"
f'<tr><td class="desc">{left_content}</td>'
f'<td class="env">{right_content}</td>'
f'<td class="team">{team_text}</td></tr>'
f"<thead><tr><th>業務内容</th><th>スキルセット</th></tr></thead>"
f'<tbody><tr><td class="desc">{left_content}</td>'
f'<td class="env">{right_content}</td></tr></tbody>'
f"</table></div>"
)

Expand Down Expand Up @@ -223,7 +229,7 @@ def _build_html(resume: dict) -> str:
client_name = _a(client, "name")
if client_name:
parts.append(
f'<div class="client-name">' f"取引先名:{_esc(client_name)}</div>",
f'<div class="client-name">' f"案件名:{_esc(client_name)}</div>",
)
projects = _a(client, "projects", [])
for proj in projects:
Expand Down
22 changes: 16 additions & 6 deletions backend/app/services/pdf/templates/resume.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
}

body {
margin: 0;
font-family: "NotoSansJP", sans-serif;
font-size: 9pt;
line-height: 1.6;
Expand All @@ -39,13 +40,17 @@ h2 {
border-bottom: 2px solid #333;
padding-bottom: 1mm;
margin: 6mm 0 3mm 0;
/* 見出しがページ末尾に孤立して直後の本文だけ次ページに行くのを防ぐ */
page-break-after: avoid;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check WeasyPrint documentation for break-after support
python -c "import weasyprint; print(f'WeasyPrint version: {weasyprint.__version__}')"

Repository: yusuke0610/devforge

Length of output: 187


🏁 Script executed:

#!/bin/bash
# Locate all page-break-after usages in the CSS template and show nearby context.
rg -n "page-break-after\s*:\s*avoid" backend/app/services/pdf/templates/resume.css || true
# Also show any break-after usages for comparison.
rg -n "break-after\s*:\s*avoid" backend/app/services/pdf/templates/resume.css || true

# Print the relevant line ranges around the originally referenced lines (43-44, 62-63, 114-115).
# Use sed with absolute line numbers from the file.
sed -n '35,75p' backend/app/services/pdf/templates/resume.css
echo "----"
sed -n '95,130p' backend/app/services/pdf/templates/resume.css

Repository: yusuke0610/devforge

Length of output: 1635


🏁 Script executed:

#!/bin/bash
# Locate all page-break-after usages in the CSS template and show nearby context.
rg -n "page-break-after\s*:\s*avoid" backend/app/services/pdf/templates/resume.css || true
# Also show any break-after usages for comparison.
rg -n "break-after\s*:\s*avoid" backend/app/services/pdf/templates/resume.css || true

# Print the relevant line ranges around the originally referenced lines (43-44, 62-63, 114-115).
# Use sed with absolute line numbers from the file.
sed -n '35,75p' backend/app/services/pdf/templates/resume.css
echo "----"
sed -n '95,130p' backend/app/services/pdf/templates/resume.css

Repository: yusuke0610/devforge

Length of output: 1635


🌐 Web query:

WeasyPrint support for break-after CSS Fragmentation Module Level 3 page-break-after

💡 Result:

WeasyPrint support for “break-after” from the CSS Fragmentation Module Level 3 (and CSS2’s page-break-after alias) is only for page fragmentation, not for multi-column/region fragmentation. 1) Fragmentation Module Level 3: break-after is supported for pages WeasyPrint’s documentation states that the “break-before”, “break-after”, and “break-inside” properties are supported, specifically for pages (but not for columns and regions). [1] So, in terms of the exact question (“break-after CSS Fragmentation Module Level 3 … page-break-after”): - break-after: page (and related break-after values that create page breaks) should work because break-after is supported for pages. [1][2] - page-break-after (CSS2 alias) is also supported. [1] 2) Not supported for columns and regions The same WeasyPrint docs explicitly say break properties are not supported “for columns and regions”. [1] So if you are inside a multi-column container or other fragmentation context where columns/regions apply, break-after may not behave as expected (and WeasyPrint’s multi-column docs also note break-before/after/inside are not supported for multi-column layout). [3] 3) Practical note for your template pattern In a simple single-column paged flow, using page-break-after: always should usually force page breaks after each repeated section. However, real-world behavior can be affected by surrounding layout (e.g., margins, flex/grid/other supported-but-not-perfect layout scenarios, and where in the DOM the break element lands). If you’re trying to force a break after each “section”, a common reliable pattern is to apply the break to an element that produces/ends the section content and is not empty (e.g., a block-level element wrapping the section), and to test with a single instance first. (Your own experience of “it only works when I add it twice” can happen with edge cases where the break opportunity is suppressed or where the added element creates additional layout that changes break positioning.) Bottom line - Yes: WeasyPrint supports break-after (Fragmentation Module Level 3) for pages, and supports page-break-* aliases too. [1] - No: It does not support break-after for columns and regions. [1][3] If you share whether your template is single-column or uses multi-columns (or any CSS that creates column/region fragmentation), I can suggest the most WeasyPrint-compatible way to structure the break element.

Citations:


🌐 Web query:

WeasyPrint page-break-after break-after

💡 Result:

WeasyPrint supports both the modern break-after CSS property and the legacy page-break-after alias [1]. Key points regarding their usage: 1. Compatibility: WeasyPrint supports the properties defined in the CSS Fragmentation Module (Level 3/4), specifically break-before, break-after, and break-inside [1]. It also maintains support for legacy page-break-* aliases, such as page-break-after [1]. 2. Preferred Usage: While both work, break-after is the modern standard [2][3]. When using these properties, common values include always (to force a break), avoid (to prevent a break), and position-specific values like left or right [1][4][2]. 3. Troubleshooting Common Issues: - Empty Pages: If you experience unexpected blank pages, ensure that you are not applying breaks to elements that are already at the end of a page or applying them in a way that forces multiple breaks unintentionally [5]. - Box Model Effects: WeasyPrint relies on the box model to determine break points [6]. Applying a break property to an empty element (e.g., <p style="page-break-after: always;"></p>) can sometimes lead to unpredictable behavior if the element itself creates layout interference. - Alternative Approaches: If a standard break is not working as expected within complex layouts (like tables), try applying the CSS property directly to the block-level element (e.g., a or

) instead of using an empty spacer element [7]. For official technical reference, you can consult the WeasyPrint documentation on API and layout [1][6].

Citations:


🌐 Web query:

CSS Fragmentation Module Level 3 page-break-after deprecated break-after avoid

💡 Result:

The page-break-after property is deprecated and has been superseded by the break-after property [1][2][3]. As part of the CSS Fragmentation Module Level 3 specification, browsers are required to treat page-break-after as a legacy shorthand alias for break-after to maintain backward compatibility [4][5][6]. While it remains supported for compatibility reasons, its use is no longer recommended, and developers are encouraged to update their code to use break-after [1][3]. The following table summarizes how the legacy property maps to the modern property: Legacy (page-break-after) | Modern (break-after) --- | --- auto | auto left | left right | right avoid | avoid always | page Note that the legacy always value is aliased to page in the modern specification [5][6]. The break-after property is more robust, as it extends beyond paged media (printing) to support fragmentation in multi-column layouts and regions [2][6].

Citations:


Replace deprecated page-break-after with modern break-after

backend/app/services/pdf/templates/resume.css uses page-break-after: avoid in h2, .company-header, and .client-name (lines 44, 63, 115). page-break-after is deprecated in favor of CSS Fragmentation Level 3 break-after; WeasyPrint supports both, so this keeps behavior while aligning with the current standard.

♻️ Proposed fix
 h2 {
   font-size: 12pt;
   border-bottom: 2px solid `#333`;
   padding-bottom: 1mm;
   margin: 6mm 0 3mm 0;
   /* 見出しがページ末尾に孤立して直後の本文だけ次ページに行くのを防ぐ */
-  page-break-after: avoid;
+  break-after: avoid;
 }
 .company-header {
   background: `#d6dce8`;
   padding: 3px 4px;
   font-size: 10pt;
   font-weight: bold;
   border-bottom: 0.3px solid `#999`;
   /* ヘッダがページ末尾に孤立し本文だけ次ページに分かれるのを防ぐ */
-  page-break-after: avoid;
+  break-after: avoid;
 }
 .client-name {
   font-size: 10pt;
   font-weight: bold;
   margin: 3mm 0 2mm 0;
   padding-bottom: 1mm;
   border-bottom: 2px solid `#333`;
   /* 案件名見出しの孤立防止 */
-  page-break-after: avoid;
+  break-after: avoid;
 }
🧰 Tools
🪛 Stylelint (17.12.0)

[error] 44-44: Expected "page-break-after" to be "break-after" (property-no-deprecated)

(property-no-deprecated)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/services/pdf/templates/resume.css` at line 44, Replace the
deprecated CSS property page-break-after with the modern break-after in the
resume stylesheet: for selectors h2, .company-header, and .client-name, change
the rule page-break-after: avoid to break-after: avoid so the behavior is
preserved while using the CSS Fragmentation Level 3 property (WeasyPrint
supports both).

}

/* 会社ブロック */
.company {
border: 0.5px solid #666;
margin-bottom: 3mm;
page-break-inside: avoid;
/* 会社ブロック全体を 1 ページに固めると、収まらない時に丸ごと次ページへ送られ、
直前(職務要約など)に大きな空白が残る。ページ跨ぎを許可してページを詰める。
代わりに分割を避けるのは下位の .project 単位で行う。 */
}

.company-header {
Expand All @@ -54,6 +59,8 @@ h2 {
font-size: 10pt;
font-weight: bold;
border-bottom: 0.3px solid #999;
/* ヘッダがページ末尾に孤立し本文だけ次ページに分かれるのを防ぐ */
page-break-after: avoid;
}

.company-info {
Expand Down Expand Up @@ -104,11 +111,17 @@ h2 {
margin: 3mm 0 2mm 0;
padding-bottom: 1mm;
border-bottom: 2px solid #333;
/* 案件名見出しの孤立防止 */
page-break-after: avoid;
}

/* プロジェクトテーブル */
.project {
margin-bottom: 2mm;
/* 案件が残りスペースに収まらない時に丸ごと次ページへ送られると、
枠付き company 内に大きな余白が残る。ページを詰めるため分割を許可する
(長い案件はページ境界で割れるが、余白を残すより詰めることを優先)。
分割時の列見出しは project-table の thead で繰り返す。 */
}

.project-header {
Expand Down Expand Up @@ -139,17 +152,14 @@ h2 {
}

.project-table td.desc {
width: 60%;
/* 体制カラムを廃止した分を業務内容へ割り当てる */
width: 75%;
}

.project-table td.env {
width: 25%;
}

.project-table td.team {
width: 15%;
}

/* 業務内容カラム内のラベル */
.project-table td.desc p {
margin: 0 0 0.5em 0;
Expand Down
14 changes: 7 additions & 7 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ export function CareerExperienceEditor({
</>
}
>
<div className={shared.inline}>
{/* 会社名:事業内容:IT企業 = 4.5:5:0.5 の幅比で配置 */}
<div className={shared.inline} style={{ gridTemplateColumns: "4.5fr 5fr 0.5fr" }}>
<label>
{/* グローバル CSS で label { display: grid } のため、テキストと DirtyDot を span で
束ねないと別々の行になる。span で 1 グリッド行に束ねることでラベル右側に並べる。 */}
Expand Down Expand Up @@ -115,6 +116,26 @@ export function CareerExperienceEditor({
placeholder="例: SES事業、受託開発"
/>
</label>
{/* IT企業かどうか(非ITは案件を持たず詳細のみ)。会社名・事業内容と同じ行に配置。
上段は labelText のスペーサで隣のラベル行と高さを合わせ、下段のトグルを入力欄と横並びにする。 */}
<label>
<span className={shared.labelText} aria-hidden="true">
&nbsp;
</span>
<span className={styles.companyTypeToggle}>
<input
type="checkbox"
checked={exp.is_it_company}
onChange={(e) =>
onUpdateExperienceField(expIndex, "is_it_company", e.target.checked)
}
/>
<span>
IT企業
<DirtyDot visible={Boolean(fieldDirty?.is_it_company)} />
</span>
</span>
</label>
</div>

<div className={shared.inline}>
Expand Down Expand Up @@ -215,19 +236,6 @@ export function CareerExperienceEditor({
</label>
</div>

{/* IT企業かどうか(非ITは取引先を持たず詳細のみ) */}
<label className={styles.clientCheckbox}>
<input
type="checkbox"
checked={exp.is_it_company}
onChange={(e) => onUpdateExperienceField(expIndex, "is_it_company", e.target.checked)}
/>
<span>
IT企業
<DirtyDot visible={Boolean(fieldDirty?.is_it_company)} />
</span>
</label>

{!exp.is_it_company && (
<div className={styles.stackSection}>
<label>
Expand All @@ -250,7 +258,7 @@ export function CareerExperienceEditor({

{exp.is_it_company && (
<div className={styles.stackSection}>
<h3>取引先</h3>
<h3>案件情報</h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incomplete terminology migration creates user confusion.

The section heading now reads "案件情報" (Project Information), but the action button below at line 280 still says "取引先を追加" (Add Client). This inconsistency confuses users—are they adding "案件" or "取引先"?

For a coherent user experience, either complete the terminology migration throughout (all "取引先" → "案件") or reconsider whether the heading should remain "取引先情報" to match the button text.

Proposed fix for terminology consistency

If the intent is to fully migrate to "案件" terminology:

-            <button type="button" className="ghost" onClick={() => onAddClient(expIndex)}>
-              取引先を追加
-            </button>
+            <button type="button" className="ghost" onClick={() => onAddClient(expIndex)}>
+              案件を追加
+            </button>

And similarly update ClientEditor.tsx line 232:

-        {client.is_vacation ? "休業を削除" : "取引先を削除"}
+        {client.is_vacation ? "休業を削除" : "案件を削除"}

And line 91:

-              取引先なし
+              案件なし
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/forms/CareerFormEditors/CareerExperienceEditor.tsx`
at line 261, The heading and action button in CareerExperienceEditor are
inconsistent: the h3 shows "案件情報" while the action button still reads "取引先を追加";
pick one terminology and make labels consistent across the component and related
editors (e.g., CareerExperienceEditor and ClientEditor). Either change the h3 to
"取引先情報" or change all button/label text "取引先を追加"/"取引先" to "案件を追加"/"案件" (and
update any shared label props, translation keys or constants used by
ClientEditor and CareerExperienceEditor) so the UI uses a single term
throughout.

{exp.clients.map((client, clientIndex) => (
<ClientEditor
key={`client-${expIndex}-${clientIndex}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function ClientEditor({
{!client.is_vacation && client.has_client && (
<label className={styles.clientNameLabel}>
<span>
取引先名(呼称)
案件名

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for remaining "取引先" usage in frontend form components
rg -n --type=tsx -C2 '取引先' frontend/src/components/forms/

Repository: yusuke0610/devforge

Length of output: 91


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Correctly search for remaining Japanese terminology in the target area
echo "== occurrences of '取引先' in frontend/src/components/forms/ (ts/tsx/js/ts?) =="
rg -n -C2 -g'*.{ts,tsx,js,jsx}' '取引先' frontend/src/components/forms/ || true

# 2) Inspect the specific file around the cited lines
echo
echo "== ClientEditor.tsx around label change =="
# show manageable ranges around the referenced line numbers
nl -ba frontend/src/components/forms/CareerFormEditors/ClientEditor.tsx | sed -n '50,120p'

echo
echo "== ClientEditor.tsx around line 220-260 =="
nl -ba frontend/src/components/forms/CareerFormEditors/ClientEditor.tsx | sed -n '210,260p'

# 3) Open the frontend message management guidelines to confirm SSoT / literal rules
echo
echo "== .claude/rules/frontend/messages.md =="
nl -ba .claude/rules/frontend/messages.md | sed -n '1,220p'

# 4) Also confirm if there's any message/i18n pattern used in this file
echo
echo "== ClientEditor.tsx: find uses of i18n/message helpers =="
rg -n 'useTranslation|t\(|message\.|i18n|SSoT|messages' frontend/src/components/forms/CareerFormEditors/ClientEditor.tsx || true

echo
echo "== ClientEditor.tsx: find occurrences of '案件名' =="
rg -n '案件名' frontend/src/components/forms/CareerFormEditors/ClientEditor.tsx || true

Repository: yusuke0610/devforge

Length of output: 7667


🏁 Script executed:

#!/bin/bash
set -euo pipefail

TARGET="frontend/src/components/forms/CareerFormEditors/ClientEditor.tsx"
FORMS_DIR="frontend/src/components/forms/"

echo "== occurrences of '取引先' in $FORMS_DIR (ts/tsx/js/jsx) =="
rg -n -C2 -t ts -t tsx -t js -t jsx '取引先' "$FORMS_DIR" || true

echo
echo "== occurrences of '案件名' in $TARGET =="
rg -n '案件名' "$TARGET" || true

echo
echo "== ClientEditor.tsx line 60-110 (around the label change) =="
awk 'NR>=60 && NR<=110 {printf "%d\t%s\n", NR, $0}' "$TARGET"

echo
echo "== ClientEditor.tsx line 210-250 (around delete button) =="
awk 'NR>=210 && NR<=250 {printf "%d\t%s\n", NR, $0}' "$TARGET"

echo
echo "== .claude/rules/frontend/messages.md (message-literal / SSoT rules) =="
# Print only the sections that mention literals/SSoT/i18n to keep output small
rg -n 'SSoT|literal|message|i18n|t\(' .claude/rules/frontend/messages.md || true
awk 'NR>=1 && NR<=260 {printf "%d\t%s\n", NR, $0}' .claude/rules/frontend/messages.md

Repository: yusuke0610/devforge

Length of output: 7603


Fix remaining “client” terminology + move UI strings to frontend SSoT messages

  • ClientEditor.tsx は「案件名」へ変えている一方、UI はまだ「取引先なし」(line 91)や「取引先を削除」(line 232)を表示しており、文言が混在してユーザーが迷います(“案件名” 側の用語に統一するか、意図を明確に)。
  • .claude/rules/frontend/messages.md に従い、ts/tsx のユーザー表示文言はリテラル直書き禁止です。案件名(line 70)、取引先なし(line 91)、取引先を削除(line 232)、placeholder="例: 〇〇社(略称)"(line 77)などを frontend/src/constants/messages.ts から取得する形に置き換えてください。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/forms/CareerFormEditors/ClientEditor.tsx` at line 70,
ClientEditor currently mixes “案件名” UI with leftover “取引先…” literals and
hard-coded strings; update ClientEditor.tsx to use single terminology (“案件名”)
consistently by replacing occurrences of "取引先なし" and "取引先を削除" with the
equivalent “案件名” wording, and move all user-facing literals (e.g. "案件名",
"取引先なし", "取引先を削除", and placeholder "例: 〇〇社(略称)") into
frontend/src/constants/messages.ts following .claude/rules/frontend/messages.md,
then import the message constants into ClientEditor.tsx and use them in the JSX
(replace the literal strings and placeholder props). Ensure the message keys you
add are clearly named (e.g. CASE_NAME, CASE_NONE, CASE_DELETE_LABEL,
CASE_PLACEHOLDER) and update any labels/placeholders in ClientEditor component
to reference those keys so terminology is consistent.

<DirtyDot visible={Boolean(dirty?.self)} />
</span>
<input
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/components/forms/CareerResumeForm.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@
white-space: nowrap;
}

/* 会社名・事業内容と同じグリッド行に並べる IT企業トグル。
ラベル(グローバル label { display: grid })の下段にこのトグルを置き、
入力欄と同じ高さ・同じ段で横並びにする(上段は labelText スペーサで高さを合わせる)。 */
.companyTypeToggle {
display: inline-flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
font-size: 0.9rem;
/* 入力欄の高さに合わせる */
height: 2.6rem;
white-space: nowrap;
}

/* split の親。コンテナクエリの基準にする(split に実際に割り当てられる幅で縦積み判定)。
ビューポート幅ではなくこの幅で判定することで、サイドバー展開/折りたたみにも追従する。 */
.splitWrap {
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/forms/ProjectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type ProjectModalProps = {
onSave: (project: CareerProjectForm) => void;
/** 閉じるコールバック */
onClose: () => void;
/** カテゴリごとの技術スタック名称リスト */
/** カテゴリごとのスキルセット名称リスト */
techStackNamesByCategory: Map<string, string[]>;
/**
* PDF 取り込み補助。PDF が選択されている時はモーダル内の右カラムに原本ビューを再掲する。
Expand Down Expand Up @@ -268,10 +268,10 @@ export function ProjectModal({
labelAdornment={<DirtyDot visible={dirty.fields.description} />}
/>

{/* 技術スタック */}
{/* スキルセット */}
<div className={styles.stackSection}>
<h3>
技術スタック ※プルダウンにないものはテキストで入力できます。
スキルセット ※プルダウンにないものはテキストで入力できます。
<DirtyDot visible={dirty.technology_stacks} />
</h3>
<div className={styles.stackGrid}>
Expand Down Expand Up @@ -299,7 +299,7 @@ export function ProjectModal({
type="button"
className={styles.chipRemove}
onClick={() => removeTechStack(stackIndex)}
aria-label="技術スタックを削除"
aria-label="スキルセットを削除"
>
&times;
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ export function CareerQualificationsSection({
const rowDirty = qualificationsDirty?.[index];
return (
<div key={`qualification-${index}`} className={shared.entry}>
<div className={shared.inline}>
{/* 資格名:取得日 = 7:3 の幅比で配置 */}
<div className={shared.inline} style={{ gridTemplateColumns: "7fr 3fr" }}>
<label>
<span className={shared.labelText}>
資格名
Expand Down
Loading