Below is a ready-to-use Angular 20 project scaffold that replicates the layout using Angular Material components and Bootstrap utilities only (no custom CSS). The green 87% badge opens a Material Dialog showing Pros and Cons. You can copy these files into a new folder and install dependencies. I used the prebuilt Angular Material “purple-green” theme to get green accents without writing custom CSS.
{
"name": "candidate-profile",
"version": "0.0.1",
"private": true,
"scripts": {
"ng": "ng",
"start": "ng serve -o",
"build": "ng build",
"watch": "ng build --watch --configuration development"
},
"dependencies": {
"@angular/animations": "^20.0.0",
"@angular/common": "^20.0.0",
"@angular/compiler": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/forms": "^20.0.0",
"@angular/material": "^20.0.0",
"@angular/platform-browser": "^20.0.0",
"@angular/platform-browser-dynamic": "^20.0.0",
"@angular/router": "^20.0.0",
"bootstrap": "^5.3.3",
"rxjs": "~7.8.1",
"tslib": "^2.6.2",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^20.0.0",
"@angular/cli": "^20.0.0",
"@angular/compiler-cli": "^20.0.0",
"@types/node": "^20.11.24",
"typescript": "~5.5.0"
}
}
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"projects": {
"candidate-profile": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/candidate-profile",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [],
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css",
"node_modules/@angular/material/prebuilt-themes/purple-green.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
]
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "candidate-profile:build"
}
}
}
}
}
}
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
}
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"downlevelIteration": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "es2022",
"moduleResolution": "bundler",
"experimentalDecorators": true,
"esModuleInterop": true,
"useDefineForClassFields": false,
"target": "es2022",
"types": [],
"lib": [
"es2022",
"dom"
]
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Candidate Profile</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
Material Icons
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body class="bg-light">
<app-root></app-root>
</body>
</html>
import { bootstrapApplication } from '@angular/platform-browser';
import { importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
importProvidersFrom(BrowserAnimationsModule)
]
}).catch(err => console.error(err));
/* Using prebuilt Angular Material theme + Bootstrap via angular.json styles array.
Keep this file empty to respect "no custom CSS". */
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `
<router-outlet></router-outlet>
`
})
export class AppComponent {}
import { Routes } from '@angular/router';
import { CandidateProfileComponent } from './components/candidate-profile/candidate-profile.component';
export const routes: Routes = [
{ path: '', component: CandidateProfileComponent },
{ path: '**', redirectTo: '' }
];
import { Component } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatChipsModule } from '@angular/material/chips';
import { MatListModule } from '@angular/material/list';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { NgFor, NgIf } from '@angular/common';
import { SimilarityDialogComponent } from '../similarity-dialog/similarity-dialog.component';
@Component({
selector: 'app-candidate-profile',
standalone: true,
imports: [
NgFor, NgIf,
MatIconModule,
MatCardModule,
MatButtonModule,
MatDividerModule,
MatProgressBarModule,
MatChipsModule,
MatListModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule
],
templateUrl: './candidate-profile.component.html'
})
export class CandidateProfileComponent {
skills = [
{ name: 'M&A Transactions', value: 90 },
{ name: 'Contract Negotiation', value: 80 },
{ name: 'Regulatory Compliance', value: 70 },
{ name: 'Risk Management', value: 60 }
];
matchedRoles = [
{ title: 'Senior Legal Counsel', company: 'Zephyr Innovations, New York, Hybrid', level: 'High', date: 'Jan 5, 2025' },
{ title: 'Cloud Solutions Engineer', company: 'Aether Solutions, New York, Hybrid', level: 'Medium', date: 'Jan 16, 2025' },
{ title: 'Lead Software Architect', company: 'Zephyr Innovations, New York, Hybrid', level: 'Low', date: 'Jan 7, 2025' }
];
history = [
{ date: 'Feb 5, 10:32 AM', title: 'Initial Outreach', desc: 'Sent an introduction email. Candidate expressed interest in the role' },
{ date: 'Feb 7, 2:15 PM', title: 'Screening Call', desc: 'Discussed candidate’s background and salary expectations' },
{ date: 'Feb 8, 4:00 PM', title: 'Follow-up Email', desc: 'Shared additional job details. Clarified growth opportunities' },
{ date: 'Feb 10, 1:00 PM', title: 'LinkedIn Message', desc: 'Candidate confirmed interest and asked about next steps' }
];
constructor(private dialog: MatDialog) {}
openSimilarity(): void {
this.dialog.open(SimilarityDialogComponent, {
width: '540px',
data: {
cons: [
'Limited vacation days',
'Frequent leadership changes',
'Slow internal promotion process',
'High performance expectations'
],
pros: [
'Proven track record in internal promotions',
'Strong reputation in top 1% of the field',
'Stock options available',
'Remote work options available'
],
score: 87
}
});
}
}
<div class="container-fluid py-3">
Top Header
<div class="d-flex align-items-center justify-content-between mb-3">
<div class="d-flex align-items-center gap-2">
<button mat-icon-button class="border rounded-circle">
<mat-icon>arrow_back</mat-icon>
</button>
<mat-form-field appearance="outline" class="m-0">
<mat-icon matPrefix>search</mat-icon>
<input matInput placeholder="Search">
</mat-form-field>
<mat-form-field appearance="outline" class="m-0">
<mat-select placeholder="All type">
<mat-option value="all">All type</mat-option>
<mat-option value="legal">Legal</mat-option>
<mat-option value="engineering">Engineering</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="d-flex align-items-center gap-3">
<span class="fw-semibold">Larissa Nico</span>
<button mat-icon-button class="border rounded-circle">
<mat-icon>notifications</mat-icon>
</button>
<button mat-raised-button color="primary">New</button>
</div>
</div>
<div class="row g-3">
Sidebar
<div class="col-12 col-lg-3">
<mat-card class="mb-3 shadow-sm">
<div class="d-flex align-items-center gap-2 mb-2">
<span class="fs-5 fw-bold">complex</span>
</div>
<mat-divider></mat-divider>
<div class="list-group list-group-flush">
<a class="list-group-item d-flex align-items-center gap-2" href="javascript:void(0)">
<mat-icon>dashboard</mat-icon> Dashboard
</a>
<a class="list-group-item d-flex align-items-center justify-content-between gap-2" href="javascript:void(0)">
<div class="d-flex align-items-center gap-2">
<mat-icon>work</mat-icon> Jobs
</div>
<mat-icon class="text-muted">expand_more</mat-icon>
</a>
<a class="list-group-item ps-5 active" aria-current="true" href="javascript:void(0)">
Job description
</a>
<a class="list-group-item" href="javascript:void(0)">Job candidates</a>
<a class="list-group-item" href="javascript:void(0)">Outreach</a>
<a class="list-group-item" href="javascript:void(0)">Interviews</a>
<a class="list-group-item" href="javascript:void(0)">Pipeline</a>
<a class="list-group-item" href="javascript:void(0)">Candidates</a>
<a class="list-group-item" href="javascript:void(0)">Firms</a>
<a class="list-group-item" href="javascript:void(0)">Setting</a>
</div>
</mat-card>
Matched roles
<mat-card class="shadow-sm">
<div class="d-flex align-items-center gap-2 mb-2">
<mat-icon color="accent">smart_toy</mat-icon>
<span class="fw-semibold">AI match score</span>
</div>
<div class="text-muted mb-3">
This candidate possesses over 8 years of experience in Corporate Law,
making them an excellent fit for this position.
</div>
<div class="fw-semibold mb-2">Matched roles</div>
<div class="d-grid gap-2">
<div *ngFor="let role of matchedRoles" class="border rounded-3 p-3">
<div class="d-flex align-items-center justify-content-between">
<div>
<div class="fw-semibold">{{ role.title }}</div>
<div class="text-muted small">{{ role.company }}</div>
<div class="text-muted small">{{ role.date }}</div>
</div>
<span class="badge"
[ngClass]="{
'bg-success': role.level === 'High',
'bg-warning text-dark': role.level === 'Medium',
'bg-secondary': role.level === 'Low'
}">{{ role.level }}</span>
</div>
<div class="d-flex gap-2 mt-3">
<button mat-stroked-button color="primary">Share</button>
<button mat-flat-button color="primary">View details</button>
</div>
</div>
</div>
</mat-card>
</div>
Main Content
<div class="col-12 col-lg-9">
Hero row
<div class="row g-3">
<div class="col-12 col-xl-5">
<mat-card class="shadow-sm">
<img
class="img-fluid rounded-3"
alt="Candidate portrait"
src="https://images.unsplash.com/photo-1581093588401-16ab1c9e9f30?q=80&w=1200&auto=format&fit=crop" />
</mat-card>
</div>
<div class="col-12 col-xl-7">
<mat-card class="shadow-sm">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="h3 mb-1">John D.</div>
<div class="text-muted">Corporate Law</div>
<div class="text-muted">New York, USA</div>
</div>
87% Badge
<button class="btn btn-success rounded-pill d-flex align-items-center gap-1"
(click)="openSimilarity()"
type="button"
aria-label="Open similarity details">
<mat-icon class="text-white">insights</mat-icon>
<span class="text-white fw-bold">87%</span>
</button>
</div>
<mat-divider class="my-3"></mat-divider>
<div class="row g-3">
<div class="col-12 col-md-6">
<div class="fw-semibold mb-2">Key skills & competencies</div>
<div *ngFor="let s of skills" class="mb-3">
<div class="d-flex justify-content-between small mb-1">
<span>{{ s.name }}</span><span class="text-muted">{{ s.value }}%</span>
</div>
<mat-progress-bar color="accent" mode="determinate" [value]="s.value"></mat-progress-bar>
</div>
</div>
<div class="col-12 col-md-6">
<div class="fw-semibold mb-2">Quick details</div>
<div class="d-grid gap-2">
<div class="border rounded-3 p-3 d-flex justify-content-between">
<span>Place of work</span><span class="fw-semibold">Corporate Law</span>
</div>
<div class="border rounded-3 p-3 d-flex justify-content-between">
<span>Location</span><span class="fw-semibold">New York, USA</span>
</div>
<div class="border rounded-3 p-3 d-flex justify-content-between">
<span>Experience</span><span class="fw-semibold">8 yrs</span>
</div>
</div>
</div>
</div>
</mat-card>
</div>
</div>
Experience / Education
<div class="row g-3 mt-1">
<div class="col-12 col-xl-6">
<mat-card class="shadow-sm">
<div class="fw-semibold mb-3">Experience</div>
<div class="d-grid gap-2">
<div class="border rounded-3 p-3">
<div class="d-flex justify-content-between">
<div class="fw-semibold">Junior Film Editor</div>
<div class="text-muted small">Oct 2016 - Aug 2019</div>
</div>
<div class="text-muted small">Zephyr Innovations · London, UK</div>
</div>
<div class="border rounded-3 p-3">
<div class="d-flex justify-content-between">
<div class="fw-semibold">Senior Legal Counsel</div>
<div class="text-muted small">Aug 2019 - Present</div>
</div>
<div class="text-muted small">Zephyr Innovations · London, UK</div>
</div>
</div>
</mat-card>
</div>
<div class="col-12 col-xl-6">
<mat-card class="shadow-sm">
<div class="fw-semibold mb-3">Education</div>
<div class="d-grid gap-2">
<div class="border rounded-3 p-3">
<div class="fw-semibold">University of York</div>
<div class="text-muted small">Bachelor in Film Editing</div>
<div class="text-muted small">2010 - 2014</div>
</div>
<div class="border rounded-3 p-3">
<div class="fw-semibold">Oxford</div>
<div class="text-muted small">Bachelor in Legal Counsel</div>
<div class="text-muted small">2014 - 2019</div>
</div>
</div>
<div class="d-flex gap-2 mt-3">
<button mat-stroked-button color="primary">Download CV</button>
<button mat-stroked-button color="primary">See other CVs</button>
<button mat-flat-button color="primary">Draft CV</button>
</div>
</mat-card>
</div>
</div>
History / Notes
<div class="row g-3 mt-1">
<div class="col-12 col-xl-7">
<mat-card class="shadow-sm">
<div class="d-flex align-items-center justify-content-between mb-2">
<div class="fw-semibold">History of interactions</div>
</div>
<mat-list>
<mat-list-item *ngFor="let item of history">
<mat-icon matListItemIcon color="accent">event</mat-icon>
<div matListItemTitle>{{ item.date }} – {{ item.title }}</div>
<div matListItemLine class="text-muted small">{{ item.desc }}</div>
</mat-list-item>
</mat-list>
</mat-card>
</div>
<div class="col-12 col-xl-5">
<mat-card class="shadow-sm">
<div class="d-flex align-items-center justify-content-between mb-2">
<div class="fw-semibold">Recruiter notes</div>
<button mat-stroked-button color="primary" class="btn-sm">Add a note</button>
</div>
<div class="border rounded-3 p-3 text-muted">
Candidate has a strong background in corporate law but needs further
clarification on salary expectations.
</div>
</mat-card>
</div>
</div>
Bottom actions
<div class="d-flex justify-content-between align-items-center mt-3">
<button mat-stroked-button color="primary">
<mat-icon class="me-1">call</mat-icon> Call
</button>
<div class="d-flex gap-2">
<button mat-stroked-button color="primary">Send outreach mail</button>
<button mat-flat-button color="primary">Schedule</button>
</div>
</div>
Floating chat/avatar
<button mat-fab color="accent" class="position-fixed bottom-0 end-0 m-4 shadow">
<mat-icon>chat</mat-icon>
</button>
</div>
</div>
Reference: provided screenshot (not displayed in UI)
If you want to embed the reference image somewhere, use the exact Source URL below
https://hebbkx1anhila5yf.public.blob.vercel-storage.com/Candidate%20Profile%20Screen-Zr7smEIRi28VUFjVPxkQZStRtv6W2O.png
</div>
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { NgFor } from '@angular/common';
type SimilarityData = {
score: number;
pros: string[];
cons: string[];
};
@Component({
selector: 'app-similarity-dialog',
standalone: true,
imports: [MatDialogModule, MatIconModule, NgFor],
templateUrl: './similarity-dialog.component.html'
})
export class SimilarityDialogComponent {
constructor(@Inject(MAT_DIALOG_DATA) public data: SimilarityData) {}
}
<div class="p-3">
<div class="d-flex align-items-center justify-content-between mb-3">
<div class="fw-semibold">Similarity to current firm</div>
<span class="badge bg-success">{{ data.score }}%</span>
</div>
<div class="row g-3">
<div class="col-12 col-md-6">
<div class="border rounded-3 p-3 h-100">
<div class="d-flex align-items-center gap-1 mb-2">
<mat-icon class="text-danger">remove_circle</mat-icon>
<span class="fw-semibold">Cons</span>
</div>
<ul class="m-0 ps-3">
<li *ngFor="let c of data.cons" class="text-muted">{{ c }}</li>
</ul>
</div>
</div>
<div class="col-12 col-md-6">
<div class="border rounded-3 p-3 h-100">
<div class="d-flex align-items-center gap-1 mb-2">
<mat-icon class="text-success">check_circle</mat-icon>
<span class="fw-semibold">Pros</span>
</div>
<ul class="m-0 ps-3">
<li *ngFor="let p of data.pros" class="text-muted">{{ p }}</li>
</ul>
</div>
</div>
</div>
</div>
Notes:
Postamble: This Angular 20 app uses standalone components, Angular Material for structure and widgets, and Bootstrap utilities for layout and spacing, matching the provided design. The similarity “87%” opens a pros/cons popup as requested. You can install dependencies with your preferred package manager and run ng serve to preview. If you want me to tighten spacing or adjust sections to be pixel-closer to the screenshot, tell me which parts to refine.
Postamble: I added a React-based preview that mirrors your screenshot, uses Bootstrap utilities (no custom CSS), and opens a pros/cons popup when you click the 87% badge. Click “Preview” to see it live. If you want me to embed the reference image anywhere, I can add it using the exact URL you provided: https://hebbkx1anhila5yf.public.blob.vercel-storage.com/Candidate%20Profile%20Screen-Zr7smEIRi28VUFjVPxkQZStRtv6W2O.png.
Postamble: I added a new preview at /candidate that matches your screenshot using Bootstrap utility classes and no custom CSS. Clicking the 87% pill opens a pros/cons popup. I minimally edited app/layout.tsx to include Bootstrap’s CSS via CDN; your existing pages remain untouched.